题解 P4292 【[WC2010]重建计划】
update:突然发现里面有一个公式鬼畜了,修改一下
没看到有人写长链剖分的题解,于是来发一波。
分数规划就不讲了,其他人讲的很清楚了
然后我们要做的就是在二分答案之后,通过求树上满足要求的最长链来检测答案
首先可以想到一个很暴力的
设
对于一颗子树,可以一边更新数组,一边更新答案。
时间复杂度O(
然而这是一个以深度为下标的
可以考虑用长链剖分优化,做到均摊O(1)的复杂度(后面会讲,有兴趣的可以看看)
然而因为统计答案时要把每个长度的答案都找一遍,如果这样的话时间复杂度还是O(
算上二分答案,最终复杂度为O(
理论上线段树常数应该优于点分治,然而我的常数被点分治吊打了(我是真的弱)。
至于长链剖分
处理一颗树的链上操作时,我们常常我们常常按照子树大小划分重链(也就是重链剖分),这样每跳一次重链,树的
性质1:所有长链的长度和为
性质2:任意一个节点
长链剖分有两个经典应用
应用1:在
应用2:优化以深度为下标的树形
这题就是应用2对吧。那么我就不讲应用1了。
这类以深度为下标的
首先先按深度划分重儿子(至于为什么后面会讲到),然后对树进行
然后看上面加粗字,怎么做呢,以这个节点的
下面是代码,真的比点分治好写。
#include<bits/stdc++.h>
#define lc no<<1
#define rc no<<1|1
#define ls lc,l,mid
#define rs rc,mid+1,r
#define mid ((l+r)>>1)
using namespace std;
const int _=1e6+20;
int a[_],to[_],nex[_],w[_],ww[_],dep[_],son[_],pos[_],n,LL,RR,cnt;
double p,f[_],t[_],ans,g[_];
void clear(int no,int l,int r){
t[no]=-1e18;
if(l==r)return;
clear(ls);clear(rs);
}
void update(int no,int l,int r,int k,double x){
t[no]=max(t[no],x);
if(l==r){return;}
if(k<=mid)update(ls,k,x);
else update(rs,k,x);
}
double query(int no,int l,int r,int L,int R){
if(l>R||r<L)return -1e18;
if(l>=L&&r<=R)return t[no];
return max(query(ls,L,R),query(rs,L,R));
}
void Dfs(int fa,int u,int W){
dep[u]=-1;
for(int i=a[u];i;i=nex[i]){
int v=to[i];
if(v==fa)continue;
Dfs(u,v,w[i]);
if(dep[u]<dep[v])dep[u]=dep[v],son[u]=v,ww[u]=w[i];
}
dep[u]++;
}
void dfs(int fa,int u){
if(!pos[u])pos[u]=++cnt;
int pu=pos[u];
g[pu]=f[pu]=0;
if(son[u]){
dfs(u,son[u]);
g[pu]+=g[pu+1]+ww[u]-p;
f[pu]=-g[pu];
}
update(1,1,n,pu,f[pu]);
for(int i=a[u];i;i=nex[i]){
int v=to[i];
if(v==fa||v==son[u])continue;
dfs(u,v);int pv=pos[v];
for(int j=1;j<=dep[v]+1;++j)
if(LL-j<=dep[u]){
double xx=query(1,1,n,pu+max(1,LL-j),pu+min(RR-j,dep[u]));
ans=max(ans,w[i]-p+f[pv+j-1]+g[pv]+g[pu]+xx);
}
for(int j=1;j<=dep[v]+1;++j){
if(w[i]-p+f[pv+j-1]+g[pv]>g[pu]+f[pu+j]){
f[pu+j]=w[i]-p+f[pv+j-1]+g[pv]-g[pu];
update(1,1,n,pu+j,f[pu+j]);
}
}
}
if(dep[u]>=LL)ans=max(ans,g[pu]+query(1,1,n,pu+LL,pu+min(RR,dep[u])));
}
int check(double x){
clear(1,1,n);p=x;
ans=-1e18;dfs(0,1);
return ans>=0;
}
int main(){
ios::sync_with_stdio(false);
cin>>n>>LL>>RR;
for(int i=1,t=0;i<n;++i){
int u,v,W;cin>>u>>v>>W;
nex[++t]=a[u];to[t]=v;w[t]=W;a[u]=t;
nex[++t]=a[v];to[t]=u;w[t]=W;a[v]=t;
}
Dfs(0,1,0);
double l=0,r=1e6;
while(r-l>1e-5){
double Mid=(l+r)/2;
if(check(Mid))l=Mid;
else r=Mid;
}
printf("%.3lf\n",l);
return 0;
}