树上差分一般用于和树上路径有关的统计,对于一条路径\((S,T)\),我们一般修改他们的\(d[S],d[T],d[LCA]\)的值来达到目的,而答案通常通过统计子树和来完成,复杂度显然为\(O(n)\)。
例一:最大流
这题是一道树上差分的板子题。
显然用差分给对\(d[S],d[T]\)加一,\(d[LCA],d[las[LCA][0]]\)减一,然后求子树和就相当于给树上路径节点加一。
不多\(BB\),直接放代码。
// luogu-judger-enable-o2 #include<bits/stdc++.h> using namespace std; inline int read() { int f=1,w=0;char x=0; while(x<'0'||x>'9') {if(x=='-') f=-1; x=getchar();} while(x!=EOF&&x>='0'&&x<='9') {w=(w<<3)+(w<<1)+(x^48);x=getchar();} return w*f; } const int N=50010; int n,m,num_edge; int head[N<<1],d[N],las[N][21],Dep[N]; struct Edge{int next,to;} edge[N<<1]; inline void Add(int from,int to) { edge[++num_edge].next=head[from]; edge[num_edge].to=to; head[from]=num_edge; } inline void Dfs(int pos,int fa) { las[pos][0]=fa;Dep[pos]=Dep[fa]+1; for(int i=0;i<20;i++) las[pos][i+1]=las[las[pos][i]][i]; for(int i=head[pos];i;i=edge[i].next) if(edge[i].to!=fa) Dfs(edge[i].to,pos); } inline int LCA(int u,int v) { if(Dep[u]<Dep[v]) swap(u,v); for(int i=20;i>=0;i--) if(Dep[v]<=Dep[u]-(1<<i)) u=las[u][i]; if(u==v) return u; for(int i=20;i>=0;i--) if(las[u][i]!=las[v][i]) u=las[u][i],v=las[v][i]; return las[u][0]; } inline void Dfs_For_Ans(int pos,int fa) { for(int i=head[pos];i;i=edge[i].next) if(edge[i].to!=fa) Dfs_For_Ans(edge[i].to,pos),d[pos]+=d[edge[i].to]; } int main(){ #ifndef ONLINE_JUDGE freopen("A.in","r",stdin); #endif n=read();m=read(); for(int i=1,u,v;i<n;i++) u=read(),v=read(),Add(u,v),Add(v,u); Dfs(1,0); for(int i=1;i<=m;i++) { int s=read(),t=read(),lca=LCA(s,t); //printf("%d:%d\n",i,lca); d[s]++,d[t]++,d[lca]--,d[las[lca][0]]--; } int ans=-1;Dfs_For_Ans(1,0); for(int i=1;i<=n;i++) ans=max(ans,d[i]); printf("%d",ans); }
例二:NOIP2015运输计划
这一题有点意思,相当于求修改后的最长路径最短为多少,显然二分,考虑如何\(Check\)。
二分出\(s\)后,找到所有大于\(s\)的路径的重边,
然后判断一下即可。
注意在找重边时用差分做,即在\(LCA\)处减\(2\),\(S,T\)处加\(1\);
#include<bits/stdc++.h> using namespace std; inline int read() { int f=1,w=0;char x=0; while(x<'0'||x>'9') {if(x=='-') f=-1; x=getchar();} while(x!=EOF&&x>='0'&&x<='9') {w=(w<<3)+(w<<1)+(x^48);x=getchar();} return w*f; } const int N=300010; int n,m,num_edge,Tot; int d[N],Dep[N],Tep[N]; int head[N<<1],las[N][30]; struct Edge{int next,to,dis;} edge[N<<1]; struct Group{int from,to,len,lca;} Pln[N]; inline void Add(int from,int to,int dis) { edge[++num_edge].next=head[from]; edge[num_edge].dis=dis; edge[num_edge].to=to; head[from]=num_edge; } inline void Dfs(int pos,int fa) { las[pos][0]=fa,Dep[pos]=Dep[fa]+1; for(int i=0;i<25;i++) las[pos][i+1]=las[las[pos][i]][i]; for(int i=head[pos];i;i=edge[i].next) if(edge[i].to!=fa) d[edge[i].to]=d[pos]+edge[i].dis,Dfs(edge[i].to,pos); } inline int LCA(int u,int v) { if(Dep[u]<Dep[v]) swap(u,v); for(int i=25;i>=0;i--) if(Dep[u]-(1<<i)>=Dep[v]) u=las[u][i]; if(u==v) return u; for(int i=25;i>=0;i--) if(las[u][i]!=las[v][i]) u=las[u][i],v=las[v][i]; return las[u][0]; } inline void Dfs_For_CF(int pos,int fa) { for(int i=head[pos];i;i=edge[i].next) if(edge[i].to!=fa) Dfs_For_CF(edge[i].to,pos),Tep[pos]+=Tep[edge[i].to]; } inline bool Check(int s) { int Cnt=0,Max=0; memset(Tep,0,sizeof(Tep)); for(int i=1;i<=m;i++) if(Pln[i].len>s) { Tep[Pln[i].from]++,Tep[Pln[i].to]++;Cnt++; Tep[Pln[i].lca]-=2;Max=max(Max,Pln[i].len-s); } if(!Cnt) return 1;Dfs_For_CF(1,0); for(int i=2;i<=n;i++) if(Tep[i]==Cnt&&d[i]-d[las[i][0]]>=Max) return 1; return 0; } int main(){ #ifndef ONLINE_JUDGE freopen("A.in","r",stdin); #endif n=read(),m=read(); for(int i=1,u,v,d;i<n;i++) u=read(),v=read(),d=read(),Add(u,v,d),Add(v,u,d),Tot+=d; d[1]=0;Dfs(1,0); for(int i=1;i<=m;i++) { Pln[i].from=read(),Pln[i].to=read(); Pln[i].lca=LCA(Pln[i].from,Pln[i].to); Pln[i].len=d[Pln[i].from]+d[Pln[i].to]-2*d[Pln[i].lca]; } int l=0,r=Tot; while(l<r) { int mid=(l+r)>>1; if(Check(mid)) r=mid; else l=mid+1; } printf("%d",l); }