【NOIP2015】运输计划

先化边权为点权,然后二分答案mid

check的时候对于每一个mid,我们把长度超过mid的路径标记一下然后按点差分一下

假设最长的路径长度是maxn,有num条路径的长度大于mid,那么显然删掉的那条边需要贡献的次数也必须为num,否则必然会至少有一段仍然大于mid

如果maxn减去删掉的那条边还是比mid大,那么显然不行,要尝试更大的答案,否则试试更小的行不行

关于求每条路径的长度,可以用tarjan求解(但是我不会),所以这里采用了树剖求解(两个log,可能有点小卡)

  1 #include<bits/stdc++.h>
  2 #define writeln(x)  write(x),puts("")
  3 #define writep(x)   write(x),putchar(' ')
  4 using namespace std;
  5 inline int read(){
  6     int ans=0,f=1;char chr=getchar();
  7     while(!isdigit(chr)){if(chr=='-') f=-1;chr=getchar();}
  8     while(isdigit(chr)){ans=(ans<<3)+(ans<<1)+chr-48;chr=getchar();}
  9     return ans*f;
 10 }void write(int x){
 11     if(x<0) putchar('-'),x=-x;
 12     if(x>9) write(x/10);
 13     putchar(x%10+'0');
 14 }const int M = 6E+5;
 15 int head[M],ver[M],val[M],nxt[M],tot,n,m,st[M],ed[M],maxn,len[M];
 16 inline void add(int x,int y,int z){ver[++tot]=y,nxt[tot]=head[x],head[x]=tot,val[tot]=z;}
 17 namespace T_S{//树剖部分,区间求和,不带修,板子不讲了
 18     int idx[M],fa[M],dep[M],sz[M],son[M],tp[M],T,a[M],b[M];
 19     void dfs1(int x,int f){
 20         dep[x]=dep[f]+1,fa[x]=f,sz[x]=1;
 21         for(int i=head[x];i;i=nxt[i]){
 22             if(ver[i]==f)continue;
 23             b[ver[i]]=val[i];
 24             dfs1(ver[i],x);
 25             sz[x]+=sz[ver[i]];
 26             if(sz[ver[i]]>sz[son[x]])son[x]=ver[i];
 27         }
 28     }void dfs2(int x,int topf){
 29         tp[x]=topf,idx[x]=++T,a[T]=b[x];
 30         if(!son[x])return;
 31         dfs2(son[x],topf);
 32         for(int i=head[x];i;i=nxt[i])
 33             if(!idx[ver[i]])dfs2(ver[i],ver[i]);
 34     }inline int LCA(int x,int y){
 35         while(tp[x]!=tp[y]){
 36             if(dep[tp[x]]<dep[tp[y]])swap(x,y);
 37             x=fa[tp[x]];
 38         }return dep[x]>dep[y]?y:x;
 39     }
 40     #define ls (i<<1)
 41     #define rs (i<<1|1)
 42     #define mid (l+r>>1)
 43     int s[M<<2];
 44     inline void Push_Up(int i){s[i]=s[ls]+s[rs];}
 45     void Build(int i,int l,int r){
 46         if(l==r)return s[i]=a[l],void();
 47         Build(ls,l,mid),Build(rs,mid+1,r);
 48         Push_Up(i);
 49     }int Query(int i,int l,int r,int ql,int qr){
 50         if(ql<=l&&r<=qr)return s[i];
 51         int ans=0;
 52         if(ql<=mid)ans+=Query(ls,l,mid,ql,qr);
 53         if(qr>mid) ans+=Query(rs,mid+1,r,ql,qr);
 54         Push_Up(i);return ans;
 55     }
 56     #undef ls
 57     #undef rs
 58     #undef mid
 59     inline int Tree_Query(int x,int y){
 60         int ans=0;
 61         while(tp[x]!=tp[y]){
 62             if(dep[tp[x]]<dep[tp[y]]) swap(x,y);
 63             ans+=Query(1,1,n,idx[tp[x]],idx[x]);
 64             x=fa[tp[x]];
 65         }if(dep[x]>dep[y]) swap(x,y);
 66         ans+=Query(1,1,n,idx[x]+1,idx[y]);
 67         return ans;
 68     }
 69 }int s[M],num,res;
 70 void dfs(int x,int fa){
 71     for(int i=head[x];i;i=nxt[i]){
 72         if(fa==ver[i])continue;
 73         dfs(ver[i],x);
 74         s[x]+=s[ver[i]];
 75     }if(s[x]==num&&T_S::b[x]>res)res=T_S::b[x];//s[x]表示这个点被经过的次数,res表示在这个前提下最大的点权
 76 }inline bool check(int x){
 77     memset(s,0,sizeof(s));num=res=0;
 78     for(int i=1;i<=m;i++)
 79         if(len[i]>x)
 80             ++num,++s[st[i]],++s[ed[i]],s[T_S::LCA(st[i],ed[i])]-=2;//按点差分,lca要-2
 81     dfs(1,0);if(maxn-res>x)return 0;
 82     return 1;
 83 }int main(){
 84     n=read(),m=read();
 85     for(int i=1;i<n;i++){
 86         int x=read(),y=read(),z=read();
 87         add(x,y,z),add(y,x,z);
 88     }T_S::dfs1(1,0),T_S::dfs2(1,1),T_S::Build(1,1,n);
 89     for(int i=1;i<=m;i++){
 90         st[i]=read(),ed[i]=read();
 91         len[i]=T_S::Tree_Query(st[i],ed[i]);//树剖把每一段路径的长度都求出来
 92         maxn=max(maxn,len[i]);
 93     }int l=0,r=maxn,ans,mid;
 94     while(l<=r){
 95         mid=l+r>>1;
 96         if(check(mid))r=mid-1,ans=mid;
 97         else l=mid+1;
 98     }cout<<ans<<endl;
 99     return 0;
100 }
原文地址:https://www.cnblogs.com/zhenglw/p/11686313.html