• 树链剖分( 洛谷P3384 )


    我们有时候遇到这样一类题目,让我们维护树上路径的某些信息,这个时候发现我们无法用线段树或者树状数组来维护这些信息,那么我们就有着一种新的数据结构,树剖:将一棵树划分成若干条链,用数据结构去维护每条链,复杂度为O(logN)。

      剖分方法:
        盲目剖分
        随机剖分
       启发式剖分
    综合比较,启发式剖分是剖分时的最佳选择。

    将树中的边分为:轻边和重边
      定义size(X)为以X为根的子树的节点个数。
      令V为U的儿子节点中size值最大的节点,那么边(U,V)被称为重边,树中重边之外的边被称为轻边。

     轻重边路径剖分的性质

        轻边(U,V),size(V)<=size(U)/2。
       从根到某一点的路径上,不超过O(logN)条轻边,不超过O(logN)条重路径。

      重链剖分

        重链剖分的过程为2次DFS
        第一次:找重边
        第二次:连重边成重链

    1.    找重边  一次DFS,可记下所有的重边。
    2.     连重边成重链,以根节点为起点,沿重边向下拓展,拉成重链。不在当前重链上的节点,都以该节点为起点向下重新拉一条重链。

          剖分完之后,每条重链就相当于一段区间,用数据结构去维护。

         把所有的重链首尾相接,放到同一个数据结构上,然后维护这一个整体即可。

         

         修改操作
             单独修改一个点的权值
         根据新的编号直接在数据结构中修改就行了。

           修改操作       整体修改点 U和点V的路径上的权值         

    1.       如果U和V在同一条重链上  :  直接用数据结构修改tid[U]至tid[V]间的值。
    2.        如果U和V不在同一条重链上一边进行修改,一边将U和V往同一条重链上靠,然后就变成了I的情况。

         怎样把他们向一起靠?

         A.若fa[top[U]]与V在同一条重链上。

              修改点U与top[u]间的各权值,然后U跳至fa[top[u],就变成了I的情况。

        B.若U向上经过若干条重链和轻边与V在同一条重链上。

             不断地修改当前U和top[u]间的各权值,再将U跳至fa[top[U]],直到U与V在同一条重链。

       C.若U和V都是向上经过若干条重链和轻边,到达同一条重链。

              每次在点U和点V中,选择dep[top[x]]较大的点x,修改x与top[x]间的各权值,再跳至fa[top[x]],直到点U和点V在同一条重链。

         情况A、B是情况C的比较特殊的2种。

           I也只是II的特殊情况。
           所以,这一操作只要用一个过程。

    下附代码:

    #include<bits/stdc++.h>
    #define sight(c) ('0'<=c&&c<='9')
    #define LL int
    #define gc nc
    #define L(x) (x&-x)
    #define eho(x) for(int i=head[x];i;i=net[i])
    #define N 200007
    LL q1[N],q2[N],gg,sum[N],a[N],T,G;
    int tot,fall[N<<1],net[N<<1],head[N],top[N],son[N],f[N],dp[N],siz[N],mo,be[N],ed[N],ok
    ,n,m,A,B,t[N],op,x,y,z,dla,OS; 
    inline char nc(){
        static char buf[1000000],*p1=buf,*p2=buf;
        return p1==p2&&(p2=(p1=buf)+fread(buf,1,1000000,stdin),p1==p2)?EOF:*p1++;
    }
    inline void swap(int &x,int &y) {x^=y; y^=x; x^=y;}
    inline void read(LL &x){
        static char c;
        for(c=gc();!sight(c);c=gc());
        for(x=0;sight(c);c=gc()) x=x*10+c-48;
    }
    void write(LL x){if (x<10) {putchar('0'+x);return;}write(x/10); putchar('0'+x%10);}
    inline void ADd(int x,int y) {
        fall[++tot]=y; net[tot]=head[x]; head[x]=tot;
    }
    inline void add(LL &x,LL y) {
        x=x+y; if (x>=mo) x=x%mo; if (x<0) x=x%mo+mo;
    } 
    inline void Add(LL *A,int x,int dla) {for (;x<N;x+=L(x)) add(A[x],dla);}
    inline void adds(int l,int r,int x) {
        Add(q1,l,x); Add(q1,r+1,-x); Add(q2,l,l*x%mo); Add(q2,r+1,-(r+1)*x%mo);
    }
    inline LL Query(LL *A,int x){for(G=0;x;x-=L(x)) add(G,A[x]);return G;}
    inline LL qurey(int l){
        return (sum[l]+(l+1)*1ll*Query(q1,l)-Query(q2,l))%mo;
    }
    void dfs(int x,int fa){
        siz[x]=1; son[x]=-1; dp[x]=dp[fa]+1; f[x]=fa;
        eho(x) if (fall[i]^fa) {
            dfs(fall[i],x); siz[x]+=siz[fall[i]];
            if ((!(~son[x]))||siz[fall[i]]>siz[son[x]]) son[x]=fall[i]; 
        }
    }
    void dfs2(int x,int las){
        t[++ok]=x;top[x]=las; be[x]=ok; 
        if (~son[x]) dfs2(son[x],las);
        eho(x) if ((fall[i]^f[x])&&(fall[i]^son[x])) dfs2(fall[i],fall[i]); ed[x]=ok;
    }
    void apd(int x,int y,LL dla){
        while (top[x]!=top[y]) {
            if (dp[top[x]]<dp[top[y]]) swap(x,y);
            adds(be[top[x]],be[x],dla);
            x=f[top[x]];
        } if (dp[x]>dp[y]) swap(x,y);
        adds(be[x],be[y],dla);
    }
    LL query_path(int x,int y) {
        LL O=0;
        while (top[x]!=top[y]) {
            if (dp[top[x]]<dp[top[y]]) swap(x,y);
            add(O,qurey(be[x])-qurey(be[top[x]]-1));
            x=f[top[x]];
        } if (dp[x]>dp[y]) swap(x,y);
        add(O,qurey(be[y])-qurey(be[x]-1));
        return O;
    }
    int main () {
        read(n); read(m); read(OS);read(mo);
        for (int i=1;i<=n;i++) read(a[i]);
        for (int i=1;i< n;i++) {read(A); read(B); ADd(A,B); ADd(B,A); }
        dfs(OS,0); dfs2(OS,OS);
        for (int i=1;i<=n;i++) sum[i]=sum[i-1],add(sum[i],a[t[i]]);
        while (m--) {
            read(op); 
            switch (op) {
                case 1: read(x),read(y),read(dla); apd(x,y,dla); break;
                case 2: read(x),read(y); write(query_path(x,y));putchar('
    '); break;
                case 3: read(x); read(z); adds(be[x],ed[x],z);  break;
                case 4: read(x); T=qurey(ed[x])-qurey(be[x]-1); add(T,0ll); 
                 write(T); putchar('
    ');break;
            }
        } return 0;
    }

     我用了树状数组维护区间,因为这样比较方便。

     不懂的同学点这里:传送门

       

     

  • 相关阅读:
    【ZJOI 2008】 生日聚会
    BZOJ2135 刷题计划(贪心+二分)
    BZOJ2124 等差子序列(树状数组+哈希)
    BZOJ2282 SDOI2011消防/NOIP2007树网的核(二分答案+树形dp)
    BZOJ1304 CQOI2009叶子的染色(树形dp)
    BZOJ1283 序列(费用流)
    BZOJ1266 AHOI2006上学路线(最短路+最小割)
    BZOJ1041 HAOI2008圆上的整点(数论)
    BZOJ3505 CQOI2014数三角形(组合数学)
    BZOJ5206 JSOI2017原力(三元环计数)
  • 原文地址:https://www.cnblogs.com/rrsb/p/8053151.html
Copyright © 2020-2023  润新知