• 树上差分


    前置芝士: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 }
  • 相关阅读:
    深度优先搜索
    哈希算法
    双指针问题
    基本概念
    Ionic JPush极光推送二
    一条sql获取每个类别最新的一条记录
    Ionic App 更新插件cordova-plugin-app-version
    Ionic跳转到外网地址
    Ionic cordova-plugin-splashscreen
    Web API 上传下载文件
  • 原文地址:https://www.cnblogs.com/wsy107316/p/13732983.html
Copyright © 2020-2023  润新知