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