Analysis
一眼分数规划
然后就瓜起了
“如何判断是否存在一条边数在[L,R]的路径满足它的权值非负”
经过深思熟虑(网上题解)发现,通常情况下处理树上路径都可以考虑点分治
然后就蛮套路了
用一个单调队列维护一下,将子树按深度从小到大遍历(这样可以防止复杂度退化到n2)
具体实现:
f
[
i
]
f[i]
f[i]表示已经搜过的子树中,深度为
i
i
i的最大权值和
g
[
i
]
g[i]
g[i]表示当前这棵子树,深度为
i
i
i的最大权值和
然后用一个单调队列来匹配
好像也不是那么难
Code
#include<bits/stdc++.h>
#define in read()
#define re register
using namespace std;
inline int read(){
char ch;int f=1,res=0;
while((ch=getchar())<'0'||ch>'9') if(ch=='-') f=-1;
while(ch>='0'&&ch<='9'){
res=(res<<1)+(res<<3)+(ch^48);
ch=getchar();
}
return f==1?res:-res;
}
#define eps 1e-9
#define inf 1e9
const int N=1e5+10;
double f[N],g[N],w[N<<1];
int n,L,R;
int nxt[N<<1],head[N],to[N<<1],ecnt=0;
inline void add(int x,int y,double z){
nxt[++ecnt]=head[x];head[x]=ecnt;to[ecnt]=y;w[ecnt]=z;
}
double ans,r,dis[N];
bool vis[N];
int sze[N],dep[N];
void dfssize(int u,int fu){
sze[u]=1;
for(re int e=head[u],v;e;e=nxt[e])
if((v=to[e])!=fu&&!vis[v]) dfssize(v,u),sze[u]+=sze[v];
}
int getG(int u,int fu,int siz){
for(re int e=head[u],v;e;e=nxt[e])
if((v=to[e])!=fu&&!vis[v]&&sze[v]>siz) return getG(v,u,siz) ;
return u;
}
int predep,nowdep,tot,sub[N],mxdep[N];
void getdis(int u,int fu){
nowdep=max(nowdep,dep[u]);
for(re int e=head[u];e;e=nxt[e]){
int v=to[e];
if(vis[v]||v==fu) continue;
dep[v]=dep[u]+1;dis[v]=dis[u]+w[e];
getdis(v,u);
}
}
inline bool cmp(int a,int b){
return mxdep[a]<mxdep[b];
}
void getg(int u,int fu,int dep){
g[dep]=max(g[dep],dis[u]);
for(re int e=head[u];e;e=nxt[e]){
int v=to[e];
if(vis[v]||v==fu) continue;
getg(v,u,dep+1);
}
}
int q[N];
inline bool check(double mid){
for(re int i=1;i<=nowdep;++i) g[i]-=i*mid;
for(re int i=1;i<=predep;++i) f[i]-=i*mid;
int head,tail;
head=1;tail=0;bool flag=0;
int now=predep;
for(re int i=0;i<=nowdep;++i){
while((now+i)>=L&&now>=0){
while(head<=tail&&f[q[tail]]<f[now]) tail--;
q[++tail]=now;
now--;
}
while(head<=tail&&q[head]+i>R) head++;
if(head<=tail&&f[q[head]]+g[i]>=0) flag=1;
if(flag) break;
}
for(re int i=1;i<=nowdep;++i) g[i]+=i*mid;
for(re int i=1;i<=predep;++i) f[i]+=i*mid;
return flag;
}
void solve(int u){
vis[u]=1;predep=tot=0;dis[u]=0;
for(re int e=head[u];e;e=nxt[e]){
int v=to[e];
if(vis[v]) continue;
sub[++tot]=v;nowdep=0;
dis[v]=w[e];dep[v]=1;
getdis(v,u);mxdep[v]=nowdep;
}
sort(sub+1,sub+tot+1,cmp);
for(re int i=1;i<=tot;++i){
nowdep=mxdep[sub[i]];
getg(sub[i],0,1);
double l=ans,rr=r;
while(rr-l>eps){
double mid=(l+rr)/2.0;
if(check(mid)) l=mid;
else rr=mid;
}
ans=rr;predep=max(predep,nowdep);
for(re int j=1;j<=nowdep;++j) f[j]=max(f[j],g[j]),g[j]=-inf;
}
for(re int i=1;i<=predep;++i) f[i]=-inf;
for(re int e=head[u];e;e=nxt[e]){
int v=to[e];
if(vis[v]) continue;
dfssize(v,u);//每次求重心,都需要重新求一遍size!
int G=getG(v,0,sze[v]/2);
solve(G);
}
}
int main(){
n=in;L=in;R=in;ans=inf;
for(re int i=1;i<n;++i){
int a=in,b=in,c=in;
add(a,b,c);add(b,a,c);
ans=min(ans,(double)c);r=max(r,(double)c);
}
dfssize(1,0);
int G=getG(1,0,sze[1]/2);
for(re int i=1;i<=n;++i) f[i]=g[i]=-inf;
solve(G);
printf("%.3lf",ans);
return 0;
}