zoukankan      html  css  js  c++  java
  • 松鼠的新家

    [JLOI2014]松鼠的新家

    题目传送门

    题目大意:

    对给定路径上的所有点进行区间加和,最后进行单点查询。

    分析:

    一道树上差分裸题,当然也可以用树剖来做,不过因为不用进行修改,树链剖分就有些大材小用了。对于这种不用修改的区间的修改,我们最先能想到差分。

    普通差分:

    对于数组a[i],如果我们想要进行区间修改与单点查询,最暴力的做法就是n个单点修改,O(1)的单点查询。而这会使得修改的时间复杂度特别高。

    而差分则相反,其区间修改的时间复杂度为O(1),而单点查询则为O(n),且当要求输出全部节点的结果时,也仅需全部遍历一次。

    差分的原理是通过记录相邻元素的变化量,再通过前缀求和来得到单点的信息。

    设b数组为差分数组,a数组为答案数组,则有:

     
     

    而对于区间修改,我们只需要在起始点的对差分数组进行加(减),再在终点处的后一位置进行减(加)

    树上差分:

    对于树上的路径修改,我们首先把路径分成两条。先求得其LCA(最近公共祖先),将该路径分割成两条链,再对这两条链进行整体修改。

    对于树上的差分,差分数组记录的是 改点答案与子树答案总和的变化,即:

     

    其中,x为当前节点,son[x]为其子树

    于是,我们便可通过LCA与树上差分维护路径区间的修改

    设 路径为从x到y,o = lca( x , y ) ,要对这条路径上的所有节点加v,那么操作为

     
     
     
     

    这里注意,我们要对O的父亲节点的差分数组做减法,这样才能保证O点也被修改到

    本题代码如下:

    #include<cstdio>
    #include<algorithm>
    using namespace std;
    #define maxn 300010
    int tr[maxn<<2];
    int head[maxn],to[maxn<<1],nxt[maxn<<1],tot=0;
    int son[maxn],fa[maxn],top[maxn],sz[maxn],dep[maxn],idx[maxn],num=0;
    int ans[maxn];
    int pa[maxn];
    int n;
    void add(int x,int y)
    {
       to[++tot]=y;
       nxt[tot]=head[x];
       head[x]=tot;
    }
    void dfs1(int x,int f)
    {
       fa[x]=f;
       sz[x]=1;
       dep[x]=dep[f]+1;
       for(int i=head[x];i;i=nxt[i])
      {
           int y=to[i];
           if(y==f) continue;
           dfs1(y,x);
           sz[x]+=sz[y];
           if(sz[y]>sz[son[x]]) son[x]=y;
      }
    }
    void dfs2(int p,int tp)
    {
       top[p]=tp;
       if(son[p])
       dfs2(son[p],tp);
       for(int i=head[p];i;i=nxt[i])
      {
           if(to[i]!=fa[p]&&to[i]!=son[p])
           dfs2(to[i],to[i]);
      }
    }
    int lca(int x,int y)
    {
       while(top[x]!=top[y])
      {
           if(dep[top[x]]<dep[top[y]]) swap(x,y);
           x=fa[top[x]];
      }
       if(dep[x]>dep[y]) swap(x,y);
       return x;
    }
    void dfs_ans(int p)
    {
       ans[p]=tr[p];
       for(int i=head[p];i;i=nxt[i])
      {
           if(to[i]==fa[p])
           continue;
           dfs_ans(to[i]);
           ans[p]+=ans[to[i]];
      }
    }
    int main()
    {
       scanf("%d",&n);
       for(int i=1;i<=n;i++)
       scanf("%d",pa+i);
       for(int i=1;i<n;i++)
      {
           int x,y;
           scanf("%d%d",&x,&y);
           add(x,y);
           add(y,x);
      }
       dfs1(1,0);
       dfs2(1,1);
       for(int i=1;i<n;i++)
      {  
           int from=pa[i],end=pa[i+1];
           int o=lca(from,end);
           tr[o]-=1,tr[fa[o]]-=1,tr[end]+=1,tr[from]+=1;
      }
       dfs_ans(1);
       ans[pa[1]]++;
       for(int i=1;i<=n;i++)
           printf("%d ",ans[i]-1);
    }



  • 相关阅读:
    字符串实现变量映射
    纯css实现无限嵌套菜单
    flex布局嵌套之高度自适应
    easy ui 零散技巧
    js高级应用
    前端利器
    css高级应用及问题记录(持续更新)
    兼容性验证方案
    原生js判断元素是否可见
    vue-cli webpack躺坑之旅
  • 原文地址:https://www.cnblogs.com/Marcelo/p/13789554.html
Copyright © 2011-2022 走看看