題解 洛谷 P4292 【[WC2010]重建計劃】
阿新 • • 發佈:2020-08-12
先二分答案 \(mid\),使得 $\frac{val}{tot} \geqslant mid $,移項得:
\[\large val - tot \times mid \geqslant 0 \]
判定對每條邊的邊權都減去 \(mid\) 後,是否存在一條邊權和大於零的路徑即可。
考慮樹形 \(DP\),設 \(f_{x,i}\) 為以 \(x\) 為根的子樹內,從 \(x\) 向下延伸 \(i\) 條邊所形成的路徑邊權和的最大值,直接 \(DP\) 複雜度是 \(O(n^2)\) 的,無法接受。
發現狀態是和深度有關,所以考慮用長鏈剖分來優化,每次轉移時,先從重兒子繼承過來,然後再將輕兒子合併,合併輕兒子時掃一遍輕兒子所在的鏈即可。因為每個輕兒子都是其所在鏈的頂端,所以每個點都只會被掃一次,複雜度就有保證了。
因為有邊數的限制,所以合併時用線段樹來維護。長鏈剖分後進行 \(dfs\),優先遍歷重兒子,求出 \(dfs\) 序。對於 \(DP\) 狀態 \(f_{x,i}\),將其用 \(dfn_x + i\) 線上段樹上表示,這樣每個狀態都有一個對應的表示,轉移時也便於合併。
#include<bits/stdc++.h> #define maxn 200010 #define maxm 800010 #define inf 2000000000000000 #define ls (cur<<1) #define rs (cur<<1|1) #define mid ((l+r)>>1) using namespace std; template<typename T> inline void read(T &x) { x=0;char c=getchar();bool flag=false; while(!isdigit(c)){if(c=='-')flag=true;c=getchar();} while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();} if(flag)x=-x; } int n,L,R,cnt,root=1; double now,ans,l,r; int d[maxn],dep[maxn],len[maxn],son[maxn],dfn[maxn]; double val[maxn],dis[maxn],t[maxn],mx[maxm]; struct edge { int to,nxt; double v; }e[maxn]; int head[maxn],edge_cnt; void add(int from,int to,double val) { e[++edge_cnt]=(edge){to,head[from],val}; head[from]=edge_cnt,r=max(r,val); } void modify(int l,int r,int pos,double v,int cur) { if(l==r) { mx[cur]=max(mx[cur],v); return; } if(pos<=mid) modify(l,mid,pos,v,ls); else modify(mid+1,r,pos,v,rs); mx[cur]=max(mx[ls],mx[rs]); } double query(int L,int R,int l,int r,int cur) { if(L>R) return -inf; if(L<=l&&R>=r) return mx[cur]; double v=-inf; if(L<=mid) v=max(v,query(L,R,l,mid,ls)); if(R>mid) v=max(v,query(L,R,mid+1,r,rs)); return v; } void clear(int l,int r,int cur) { mx[cur]=-inf; if(l==r) return; clear(l,mid,ls),clear(mid+1,r,rs); } void dfs_son(int x,int fa) { d[x]=dep[x]=d[fa]+1; for(int i=head[x];i;i=e[i].nxt) { int y=e[i].to; if(y==fa) continue; dfs_son(y,x),dep[x]=max(dep[x],dep[y]); if(dep[y]>dep[son[x]]) son[x]=y,val[son[x]]=e[i].v; } len[x]=dep[x]-d[x]; } void dfs_dfn(int x) { dfn[x]=++cnt; if(son[x]) dfs_dfn(son[x]); for(int i=head[x];i;i=e[i].nxt) { int y=e[i].to; if(dfn[y]) continue; dfs_dfn(y); } } void dp(int x,int fa) { modify(1,n,dfn[x],dis[x],root); if(son[x]) dis[son[x]]=dis[x]+val[son[x]]-now,dp(son[x],x); for(int i=head[x];i;i=e[i].nxt) { int y=e[i].to; if(y==fa||y==son[x]) continue; dis[y]=dis[x]+e[i].v-now,dp(y,x); for(int j=1;j<=len[y]+1;++j) t[j]=query(dfn[y]+j-1,dfn[y]+j-1,1,n,root); for(int j=1;j<=min(len[y]+1,R);++j) ans=max(ans,t[j]+query(dfn[x]+L-j,min(dfn[x]+R-j,dfn[x]+len[x]),1,n,root)-2*dis[x]); for(int j=1;j<=len[y]+1;++j) modify(1,n,dfn[x]+j,t[j],root); } ans=max(ans,query(dfn[x]+L,min(dfn[x]+R,dfn[x]+len[x]),1,n,root)-dis[x]); } bool check(double m) { now=m,ans=-inf,clear(1,n,root),dp(1,0); return ans>=0; } int main() { read(n),read(L),read(R); for(int i=1;i<n;++i) { int x,y,v; read(x),read(y),read(v); add(x,y,v),add(y,x,v); } dfs_son(1,0),dfs_dfn(1); for(int i=1;i<=35;++i) { double m=(l+r)/2; if(check(m)) l=m; else r=m; } printf("%.3lf",l); return 0; }