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;
}

  

posted @ 2017-11-14 03:45  Claris  阅读(349)  评论(0编辑  收藏  举报