zoukankan      html  css  js  c++  java
  • 序列/树上差分小结 By cellur925

    首先我们需要注意一下的是,差分比较适用于修改比较多而查询比较少的情况。

    一、序列上差分

    借教室  这是一道二分答案,在check函数中用到差分技巧的一道题,譬如说我们要把一个序列中[l,r]区间都加上一个权值,我们可以把在 l 处加上这个值,在r+1处减去这个值,再对记录权值的数组求前缀和,那么我们就可以得到这个真正的权值数组。

    题解 在链接里,代码就不放了=w=。

    二、树上差分

    树上差分可以分为在点权上的情况和 在边权上的情况

    1:    点权 :

    比如在树上把 从u到v的路径的某个权值都加上一个数,设这个权值数组val,那么我们需要在val[u],val[v]上加上这个值,并在val[lca(u,v)]上减去这个值,并在val[f[lca(u,v][0]]上减去这个值。

    理论如斯

    这个算法就经常用于统计树上路径某点/某边的经过次数。

    例题1 [USACO15DEC]最大流Max Flow(不是网络流题目啦==)

    给出若干路径,求被经过此处最多的点的经过次数。

    Sol:树上倍增求LCA+树上差分记录+最后O(n)扫一遍更新。

    总复杂度O(nlogn预处理+klogn求LCA+n更新)

     1 #include<cstdio>
     2 #include<algorithm>
     3 #include<cmath>
     4 #include<queue>
     5 #define maxn 500900
     6 
     7 using namespace std;
     8 
     9 int n,k,t,ans,tot;
    10 int head[maxn],d[maxn],val[maxn],f[maxn][20];
    11 struct node{
    12     int to,next;
    13 }edge[maxn*2];
    14 
    15 void add(int x,int y)
    16 {
    17     edge[++tot].next=head[x];
    18     head[x]=tot;
    19     edge[tot].to=y;
    20 }
    21 
    22 void init_LCA()
    23 {
    24     queue<int>q;
    25     q.push(1);
    26     d[1]=1;
    27     while(!q.empty())
    28     {
    29         int u=q.front();
    30         q.pop();
    31         for(int i=head[u];i;i=edge[i].next)
    32         {
    33             int v=edge[i].to;
    34             if(d[v]) continue;
    35             d[v]=d[u]+1;
    36             f[v][0]=u;
    37             for(int j=1;j<=t;j++)
    38                 f[v][j]=f[f[v][j-1]][j-1];
    39             q.push(v);
    40         }
    41     }
    42 }
    43 
    44 int LCA(int x,int y)
    45 {
    46     if(d[x]>d[y]) swap(x,y);
    47     for(int i=t;i>=0;i--)
    48         if(d[f[y][i]]>=d[x]) y=f[y][i];
    49     if(x==y) return x;
    50     for(int i=t;i>=0;i--)
    51         if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
    52     return f[x][0];
    53 }
    54 
    55 void review(int u,int fa)
    56 {
    57     for(int i=head[u];i;i=edge[i].next)
    58     {
    59         int v=edge[i].to;
    60         if(v==fa) continue;
    61         review(v,u);
    62         val[u]+=val[v];
    63     }
    64     ans=max(ans,val[u]);
    65 }
    66 
    67 int main()
    68 {
    69     scanf("%d%d",&n,&k);
    70     t=log2(n)+1;
    71     for(int i=1;i<=n-1;i++)
    72     {
    73         int x=0,y=0;
    74         scanf("%d%d",&x,&y);
    75         add(x,y),add(y,x);
    76     }
    77     init_LCA();
    78     while(k--)
    79     {
    80         int x=0,y=0;
    81         scanf("%d%d",&x,&y);
    82         int fa=LCA(x,y);
    83 //        printf("%d
    ",fa);
    84         val[x]++;val[y]++;
    85         val[fa]--;val[f[fa][0]]--;
    86     }
    87     review(1,0);
    88     printf("%d",ans);
    89     return 0;
    90 }
    View Code

    例题2  [JLOI2014]松鼠的新家

    也是求点经过次数的=w=,稍有变动,需要在最后从第2个到达点到最后一个到达点的权值都减。(题意使然)

     1 #include<cstdio>
     2 #include<algorithm>
     3 #include<cmath>
     4 #include<queue>
     5 #define maxn 300090
     6 
     7 using namespace std;
     8 
     9 int n,tot,t;
    10 int seq[maxn],head[maxn],val[maxn],d[maxn],f[maxn][20];
    11 struct node{
    12     int to,next;
    13 }edge[maxn*2];
    14 
    15 void add(int x,int y)
    16 {
    17     edge[++tot].to=y;
    18     edge[tot].next=head[x];
    19     head[x]=tot;
    20 }
    21 
    22 void LCA_prework()
    23 {
    24     queue<int>q;
    25     q.push(1),d[1]=1;
    26     while(!q.empty())
    27     {
    28         int u=q.front();q.pop();
    29         for(int i=head[u];i;i=edge[i].next)
    30         {
    31             int v=edge[i].to;
    32             if(d[v]) continue;
    33             d[v]=d[u]+1;
    34             f[v][0]=u;
    35             for(int j=1;j<=t;j++)
    36                 f[v][j]=f[f[v][j-1]][j-1];
    37             q.push(v);
    38         }
    39     } 
    40 }
    41 
    42 int LCA(int x,int y)
    43 {
    44     if(d[x]>d[y]) swap(x,y);
    45     for(int i=t;i>=0;i--)
    46         if(d[f[y][i]]>=d[x]) y=f[y][i];
    47     if(x==y) return x;
    48     for(int i=t;i>=0;i--)
    49         if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
    50     return f[x][0];
    51 }
    52 
    53 void review(int u,int fa)
    54 {
    55     for(int i=head[u];i;i=edge[i].next)
    56     {
    57         int v=edge[i].to;
    58         if(v==fa) continue;
    59         review(v,u);
    60         val[u]+=val[v];
    61     }
    62 }
    63 
    64 int main()
    65 {
    66     scanf("%d",&n);
    67     t=log2(n)+1;
    68     for(int i=1;i<=n;i++) scanf("%d",&seq[i]);
    69     for(int i=1;i<=n-1;i++)
    70     {
    71         int x=0,y=0;
    72         scanf("%d%d",&x,&y);
    73         add(x,y),add(y,x);
    74     }
    75     LCA_prework();
    76     for(int i=1;i<=n-1;i++)
    77     {
    78         int fa=LCA(seq[i],seq[i+1]);
    79         val[seq[i]]++;val[seq[i+1]]++;
    80         val[fa]--;val[f[fa][0]]--;
    81     }
    82     review(1,0);
    83     for(int i=2;i<=n;i++) val[seq[i]]--;
    84     for(int i=1;i<=n;i++) printf("%d
    ",val[i]);
    85     return 0;
    86 }
    View Code

    (话说省选出板子题真的好么==)

     

    2:    边权

    给你一棵树,有n次修改操作,每次把u..v的路径权值加x,最后问从x..y的路径权值和。

     例如有一次操作是把红点(u)到绿点(v)之间的路径全部加x。那么我就标记dlt[u]+=x,dlt[v]+=x。然后我们要在lca(u,v)处标记dlt[lca(u,v)]-=2x。这样就使得加x的效果只局限在u..v,不会向lca(u,v)的爸爸蔓延。

    上面的话引用自@Sagittariusdalao。

    例题:NOIp2015运输计划

     val[x]就是x与它的父节点之间的“树边”被覆盖的次数

    链接中有详细的题解==

    小结:树上差分和树上倍增还是比较实用的,也比较灵活,需要加强举一反三能力== 

  • 相关阅读:
    Vue部署到nginx刷新后出现404页面的问题
    移动端rem布局方案
    Vue使用Mint-ui的Popup, Picker组件报错问题
    Vue稍微高级一点的选项卡—动态组件
    GMOJ 1283排列统计 题解
    GMOJ 1281旅行 题解
    Why Did the Cow Cross the Road I P 题解
    【USACO 2017 December Gold】A Pie for a Pie 题解
    2020.05.23【NOIP提高组】模拟 总结
    2020.05.03【NOIP提高组】模拟 总结
  • 原文地址:https://www.cnblogs.com/nopartyfoucaodong/p/9654967.html
Copyright © 2011-2022 走看看