前置芝士:here
树上差分:
典型问题描述:给定一棵有N个点的树,所有节点的权值初始时都为0。 有K次操作,每次指定两个点u , v,将 u 到 v 路径上所有点的权值都+1。 请输出K次操作完毕后权值最大的那个点的权值。
朴素思路:不用多想,最暴力的做法就是我们找到 u 到 v 路径上的所有点并+1(可以利用lca)。最后再遍历所有点查找权值最大的点并输出。这样时间复杂度为O(KN),这还不包括 lca 查找路径的时间。
求u到v的路径:求那么我们知道,如果假设我们要考虑的是从u到v的路径,u 与 v 的 lca 是 a ,那么很明显,如果路径中有一点 u′ 已经被访问了,且 u′ ≠ a ,那么 u' 的父亲也一定会被访问。所以,我们可以将路径拆分成两条链,u -> a 和 a -> v。
关于边的差分:
将边拆成两条链之后,我们便可以像差分一样来找到路径了。用 cf[ i ] 代表从 i 到 i 的父亲这一条路径经过的次数。因为关于边的差分,a 是不在其中的,所以考虑链 u -> a,则就要使cf[ u ]++,cf[ a ]−−。然后链a -> v,也是cf[ v ]++,cf[ a ]−−。(对应着一维差分的应用会比较好理解)所以合起来便是cf[ u ]++,cf[ v ]++,cf[ a ]−=2。然后,从根节点,对于每一个节点x,都有如下的步骤:
- 枚举x的所有子节点u
- dfs所有子节点u
- cf[ x ] + = cf[ u ]
那么,为什么能够保证这样所有的边都能够遍历到呢?因为我们刚刚已经说了,如果路径中有一点u′已经被访问了,且u′≠a,那么u′的父亲也一定会被访问。所以u′被访问几次,它的父亲也就因为u′被访问了几次。所以就能够找出所有被访问的边与访问的次数了。路径求交等一系列问题就是通过这个来解决的。因为每个点都只会遍历一次,所以其时间复杂度为Θ(n).
关于点的差分:
还是与和边的差分一样,对于所要求的路径,拆分成两条链。步骤也和上面一样,但是也有一些不同,因为关于点,u与v的lca是需要包括进去的,所以要把lca包括在某一条链中,用cf[ i ] 表示 i 被访问的次数。最后对 cf 数组的操作便是cf[ u ]++,cf[v]++,cf[ a ]−−,cf[ father[a] ]−−。其时间复杂度也是一样的Θ(n).
以上参考:here
点差分模板:题目链接:P3128 [USACO15DEC]Max Flow P
1 #include <bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 const int maxn = 2e5+10; 5 6 int N,K; 7 vector<int>v[maxn]; 8 int d[maxn],fa[maxn][21],tot,dfn[maxn],val[maxn]; 9 10 void dfs(int x,int _fa){ 11 fa[x][0] = _fa; 12 d[x] = d[ fa[x][0] ] + 1; 13 dfn[ ++tot ] = x; 14 for(int i=1;i<=20;i++) 15 fa[x][i] = fa[ fa[x][i-1] ][i-1]; 16 for(size_t i=0;i<v[x].size();i++){ 17 int to = v[x][i]; 18 if( to==_fa ) continue; 19 dfs(to,x); 20 } 21 } 22 23 int LCA(int x,int y){ 24 if( d[x]<d[y] ) swap(x,y); 25 for(int i=20;i>=0;i--){ 26 if( d[fa[x][i]]>=d[y] ) 27 x = fa[x][i]; 28 } 29 if(x==y) return x; 30 for(int i=20;i>=0;i--){ 31 if( fa[x][i]!=fa[y][i] ){ 32 x = fa[x][i]; 33 y = fa[y][i]; 34 } 35 } 36 return fa[x][0]; 37 } 38 39 int main() 40 { 41 scanf("%d%d",&N,&K); 42 for(int i=1;i<N;i++){ 43 int x,y; scanf("%d%d",&x,&y); 44 v[x].push_back(y); 45 v[y].push_back(x); 46 } 47 dfs(1,0); 48 for(int i=1;i<=K;i++){ 49 int x,y; scanf("%d%d",&x,&y); 50 val[x]++; 51 val[y]++; 52 int lca = LCA(x,y); 53 val[lca]--; 54 val[ fa[lca][0] ]--; 55 } 56 57 int ans=0; 58 for(int i=N;i>=1;i--){ 59 val[ fa[dfn[i]][0] ] += val[dfn[i]]; 60 } 61 for(int i=1;i<=N;i++){ 62 ans = max(ans,val[i]); 63 } 64 printf("%d ",ans); 65 66 return 0; 67 }