树上差分

前置芝士: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\n",ans);
65 
66     return 0;
67 }

 

posted @ 2020-09-25 22:38  swsyya  阅读(75)  评论(0编辑  收藏  举报

回到顶部