题目描述

题目传送门

分析

看到比值的形式就想到 \(01分数规划\),二分答案

设当前的值为 \(mids\)

如果存在\(\frac{\sum _{e \in S} v(e)}{|S|} \geq mids\)

那么 \(\sum _{e \in S} v(e)-|S| \times mids \geq 0\)

我们把每一条边的权值减去 \(mids\)

问题就变成了找出一条长度在 \([l,r]\) 之间的简单路径

是的路径的长度大于等于 \(0\)

我们可以开一个权值线段树去维护,但这样复杂度就变成了 \(nlog^3n\)

实际上,我们可以把寻找路径时的 \(dfs\) 换成 \(bfs\)

这样就相当于自动给路径的长度排好了序,可以直接用单调队列维护

因为每一次单调队列都要从 \(0\) 枚举到 \(min(r,maxdep)\)

因为你记录最大值的数组最多只会更新到 \(maxdep\),所以枚举多了没用

为了保证复杂度,我们要提前把子树按照子树内最大深度从小到大排序,使得 \(maxdep\) 尽可能小

还可以提前建出点分树,避免每一次 \(dfs\) 重新找重心浪费时间

时间复杂度 \(nlog^2n\)

代码

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<vector>
#define rg register
inline int read(){
rg int x=0,fh=1;
rg char ch=getchar();
while(ch<'0' || ch>'9'){
if(ch=='-') fh=-1;
ch=getchar();
}
while(ch>='0' && ch<='9'){
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*fh;
}
const int maxn=4e5+5;
int h[maxn],tot=1,n,m,lef,rig;
struct asd{
int to,nxt,preval;
double val;
}b[maxn];
void ad(rg int aa,rg int bb,rg int cc){
b[tot].to=bb;
b[tot].nxt=h[aa];
b[tot].preval=cc;
h[aa]=tot++;
}
struct Node{
int num,dep,id;
Node(){}
Node(rg int aa,rg int bb,rg int cc){
num=aa,dep=bb,id=cc;
}
};
std::vector<Node> pre[maxn];
bool cmp(rg Node aa,rg Node bb){
return aa.dep<bb.dep;
}
int dep[maxn];
void dfs(rg int now,rg int lat,rg int nowdep){
dep[now]=nowdep;
for(rg int i=h[now];i!=-1;i=b[i].nxt){
rg int u=b[i].to;
if(u==lat) continue;
dfs(u,now,nowdep+1);
dep[now]=std::max(dep[now],dep[u]);
}
}//预处理子树最大深度
int siz[maxn],maxsiz[maxn],rt,totsiz,jud[maxn],tim,tp;
bool vis[maxn];
std::vector<int> g[maxn];
void getroot(rg int now,rg int lat){
siz[now]=1,maxsiz[now]=0;
for(rg int i=h[now];i!=-1;i=b[i].nxt){
rg int u=b[i].to;
if(u==lat || vis[u]) continue;
getroot(u,now);
siz[now]+=siz[u];
maxsiz[now]=std::max(maxsiz[now],siz[u]);
}
maxsiz[now]=std::max(maxsiz[now],totsiz-siz[now]);
if(maxsiz[now]<maxsiz[rt]) rt=now;
}//找重心
void predfs(rg int now){
vis[now]=1;
for(rg int i=h[now];i!=-1;i=b[i].nxt){
rg int u=b[i].to;
if(!vis[u]){
pre[now].push_back(Node(u,dep[u],i));
}
}
std::sort(pre[now].begin(),pre[now].end(),cmp);
for(rg int i=0;i<pre[now].size();i++){
rg int u=pre[now][i].num;
totsiz=siz[u],rt=0;
getroot(u,now);
g[now].push_back(rt);
predfs(rt);
}
}//建好点分树,并按照最大深度从小到大排序
double ans=-1e7,mmax[maxn];
struct jie{
double val;
int dep;
jie(){}
jie(rg double aa,rg int bb){
val=aa,dep=bb;
}
}sta[maxn];
int q1[maxn],q2[maxn],nhead,ntail;
double q3[maxn];
void bfs(rg int now,rg double nval,rg int ndep){
tim++;
jud[now]=tim,nhead=ntail=1;
q1[1]=now,q2[1]=ndep,q3[1]=nval;
rg int now1,now2;
rg double now3;
while(nhead<=ntail){
now1=q1[nhead],now2=q2[nhead],now3=q3[nhead];
sta[++tp]=jie(now3,now2);
nhead++;
for(rg int i=h[now1];i!=-1;i=b[i].nxt){
rg int u=b[i].to;
if(jud[u]==tim || vis[u]) continue;
jud[u]=tim;
ntail++;
q1[ntail]=u,q2[ntail]=now2+1,q3[ntail]=now3+b[i].val;
}
}
}//bfs寻找路径
int q[maxn],head,tail;
void solve(rg int now){
vis[now]=1,tp=1;
rg int beg=0,cs=0,tl=0,tr=0,maxdep=0;
mmax[0]=0;
for(rg int i=0;i<pre[now].size();i++){
rg int u=pre[now][i].num;
beg=tp+1;
bfs(u,b[pre[now][i].id].val,1);
head=1,tail=0,cs=beg;
for(rg int j=std::min(rig,maxdep);j>=0;j--){
tl=j>=lef?0:lef-j,tr=rig-j;
while(head<=tail && sta[q[head]].dep<tl) head++;
while(cs<=tp && sta[cs].dep<tl) cs++;
while(cs<=tp && sta[cs].dep<=tr){
while(head<=tail && sta[cs].val>=sta[q[tail]].val) tail--;
q[++tail]=cs++;
}
if(head<=tail) ans=std::max(ans,sta[q[head]].val+mmax[j]);
}
for(rg int j=beg;j<=tp;j++) mmax[sta[j].dep]=std::max(mmax[sta[j].dep],sta[j].val);
maxdep=std::max(maxdep,sta[tp].dep);
}
if(ans>=0) return;
for(rg int i=1;i<=tp;i++) mmax[sta[i].dep]=-1e9;
for(rg int i=0;i<g[now].size();i++) solve(g[now][i]);
}//点分治核心+单调队列
bool pd(rg double mids){
memset(vis,0,sizeof(vis));
memset(jud,0,sizeof(jud));
for(rg int i=0;i<maxn;i++) mmax[i]=-1e7;
ans=-1e7,tim=0;
for(rg int i=1;i<tot;i++) b[i].val=(double)b[i].preval-mids;
solve(rt);
return ans>=0;
}//判断答案是否合法
int main(){
memset(h,-1,sizeof(h));
n=read(),lef=read(),rig=read();
rg int aa,bb,cc;
for(rg int i=1;i<n;i++){
aa=read(),bb=read(),cc=read();
ad(aa,bb,cc);
ad(bb,aa,cc);
}
dfs(1,0,0);
maxsiz[0]=0x3f3f3f3f,rt=0,totsiz=n;
getroot(1,0);
predfs(rt);
totsiz=n,rt=0;
memset(vis,0,sizeof(vis));
getroot(1,0);
double l=0,r=1e6,mids;
for(rg int i=1;i<=33;i++){
mids=(l+r)/2.0;
if(pd(mids)) l=mids;
else r=mids;
}
printf("%.3f\n",mids);
return 0;
}

最新文章

  1. CGAL4.1在VS2010上配置
  2. 【BZOJ2243】[SDOI2011]染色 树链剖分+线段树
  3. :nth-child(an+b)
  4. EntityFramework Core 学习笔记 —— 包含与排除属性
  5. System.exit(0)作用
  6. file_get_contents()获取https出现这个错误Unable to find the wrapper “https” – did
  7. Tomcat 处理请求时的中文乱码问题
  8. UVA 11825 Hackers’ Crackdown(集合动态规划 子集枚举)
  9. 找出指定目录下,大于指定大小的文件(LINUX SHELL)
  10. Android5.0 Gallery2上编译Gallery模块出错
  11. UML类图中的关系和表示方法
  12. voa 2015 / 4 / 25
  13. Java之路上,让我们Stand Up Again
  14. Logstash filter 插件之 grok
  15. eclipse invalid zip archive lib
  16. FastAdmin 基本知识流程一栏
  17. MongoDB Notes
  18. SpringBoot自定义线程池处理异步任务
  19. sql优化的方法
  20. asyncio 中给running 的loop 动态添加 Future Task

热门文章

  1. 【坑点集合】C++ STL 使用注意事项整理
  2. Java集合源码分析(八)——WeakHashMap
  3. Sense Sense (Beta)安装及解决部分Chrome插件安装时程序包无效:&quot;CRX_HEADER_INVALID&quot;
  4. HashMap 中 Key 类型的选择
  5. 04-flask-模版基础
  6. synchronized实现原理及ReentrantLock源码
  7. PHP面向对象的学习(封装,继承,多态)
  8. 【Azure Service Bus】 Service Bus如何确保消息发送成功,发送端是否有Ack机制 
  9. Windows 系统下Vue的安装及环境搭建
  10. C# 7-zip 压缩和解压缩