BZOJ4738 : 汽水
二分答案$mid$,若存在一条路径满足$|ave-k|<mid$,则答案至多为$mid-1$。
若$ave\leq k$,则$\sum(w-k)\leq 0$,且$\sum(k-w-mid)<0$;若$ave\geq k$,那么同理。
预先树分治处理出所有到重心的路径的信息,并按$w-k$排序。
那么每次检查的时候,通过双指针满足第一个限制,维护最小值来满足第二个限制。
需要注意的是,不能选取两条来自同一棵子树的路径,这只需要维护来自不同子树的最小的两条路径即可。
时间复杂度$O(n\log n(\log n+\log w))$。
#include<cstdio> #include<algorithm> using namespace std; typedef long long ll; const int N=50010,M=1000010; int n,i,x,y,g[N],nxt[N<<1],v[N<<1],ok[N<<1],ed,son[N],f[N],all,now; ll z,K,w[N<<1],val[M],L,R,mid; int st[N],en[N],cnt,cur; struct P{ll x;int y,z;P(){}P(ll _x,int _y,int _z){x=_x,y=_y,z=_z;}}q[2][M]; inline bool cmp(const P&a,const P&b){return a.x<b.x;} inline void add(int x,int y,ll z){v[++ed]=y;w[ed]=z;nxt[ed]=g[x];g[x]=ed;ok[ed]=1;} void findroot(int x,int y){ son[x]=1;f[x]=0; for(int i=g[x];i;i=nxt[i])if(ok[i]&&v[i]!=y){ findroot(v[i],x); son[x]+=son[v[i]]; if(son[v[i]]>f[x])f[x]=son[v[i]]; } if(all-son[x]>f[x])f[x]=all-son[x]; if(f[x]<f[now])now=x; } void dfs(int x,int y,ll a,int b,int c){ q[0][++cur]=P(a,b,c); q[1][cur]=P(-a,b,c); for(int i=g[x];i;i=nxt[i])if(ok[i]&&v[i]!=y)dfs(v[i],x,a+w[i]-K,b+1,c); } void solve(int x){ int i,o=++cnt; st[o]=cur+1; for(i=g[x];i;i=nxt[i])if(ok[i])dfs(v[i],x,w[i]-K,1,v[i]); en[o]=cur; for(i=0;i<2;i++)sort(q[i]+st[o],q[i]+en[o]+1,cmp); for(i=g[x];i;i=nxt[i])if(ok[i]){ ok[i^1]=0; f[0]=all=son[v[i]]; findroot(v[i],now=0); solve(now); } } inline bool check(){ for(int i=0;i<2;i++){ for(int j=1;j<=cur;j++){ val[j]=-q[i][j].x-mid*q[i][j].y; if(q[i][j].x<=0&&val[j]<0)return 1; } for(int j=1;j<=cnt;j++){ int l=st[j],r=en[j],x=r,y=l,az=0,bz=0;ll av,bv; for(;x>=l;x--){ for(;y<=r&&q[i][x].x+q[i][y].x<=0;y++){ ll v=val[y];int z=q[i][y].z; if(az==z){if(av>v)av=v;} else if(!az||av>v)bv=av,bz=az,av=v,az=z; else if(!bz||bv>v)bv=v,bz=z; } if(az&&az!=q[i][x].z&&val[x]+av<0)return 1; if(bz&&bz!=q[i][x].z&&val[x]+bv<0)return 1; } } } return 0; } int main(){ scanf("%d%lld",&n,&K); for(ed=i=1;i<n;i++)scanf("%d%d%lld",&x,&y,&z),add(x,y,z),add(y,x,z); f[0]=all=n;findroot(1,now=0);solve(now); L=1,R=1e13; while(L<=R){ mid=(L+R)>>1; if(check())R=mid-1;else L=mid+1; } return printf("%lld",R),0; }