8.4 树链剖分

详解:https://www.cnblogs.com/ivanovcraft/p/9019090.html

 

树链剖分的本质是序

以尽量走重链为序,使一棵树的结点尽量集中地分解成较少的链,而链作为连续的数据结构是易于维护的

 

剖分的过程有两遍dfs :

dfs1:找出重结点 ,确立各个点的深度 ,这个过程可以确定:sz[i] ,dep[i] ,fa[i] ,son[i]

void dfs1( int u ,int f ){
    sz[u] = 1;
    for( int i = head[u]; i ;i = edge[i].next ){
        int v = edge[i].to;
        if( v == f )continue;
        dep[v] = dep[u] + 1;
        fa[v] = u;
        dfs1( v ,u );
        sz[u] += sz[v];
        if( sz[v] >sz[ son[u] ] )son[u] = v;
    }
}

dfs2:将重结点链接成链(找出top[i]),并以这个链接的顺序作为将链存在数组中的顺序(dfn[i]),这个过程可以确定:dfn ,top ,rnk

void dfs2( int u ,int t ){
    dfn[u] = ++tot;
    top[u] = t;
    rnk[tot] = u;
    if( son[u] )dfs2( son[u] ,t );
    for( int i = head[u] ;i ;i = edge[i].next ){
        int v = edge[i].to;
        if( v!= fa[u] && v!= son[u] )dfs2(v,v);
    }
}

 

剖分后就可以利用线段树,树状数组等数据结构对分解后的链进行维护,给定两个节点x,y,进行它们之间的更新和查询操作,这个过程是:

1.如果x,y在一条重链上( top[x]==top[y] ),则可以直接利用数据结构对x到y这一段连续的链进行直接操作

2.如果x,y不在一条重链上( top[x] != top[y] ),找出较深的结点(dep[fx]>dep[fy])x,先将x到fx这一小段进行操作,再令x向其重链之上的父节点转移

重复这一过程,直到完成x到y之间完整路径的操作

这一过程也是寻找x,y LCA 的过程

int LCA( int x ,int y ){
    int fx = top[x] ,fy = top[y];
    while( fx != fy ){
        if( dep[x] < dep[y] )swap(x ,y) ,swap( fx ,fy );
        x= fa[fx] ,fx = top[x];
    }
    return dep[x] < dep[y] ? x : y ;
}
int c_query( int x ,int y ){
    int fx = top[x] ,fy = top[y];
    int ans = 0;
    while( fx != fy ){
        if( dep[fx] < dep[fy] )swap(x ,y) ,swap(fx ,fy);
        ans += query( 1 ,dfn[fx] ,dfn[x] );
        x = fa[x] ,fx = top[x];
    }
    if( x==y )return ans;
    if( dfn[x] > dfn[y] )swap( x ,y );
    if(dfn[x] != dfn[y])ans += query( 1 ,dfn[son[x]] ,dfn[y] );
    return ans ;
}

 


线段树: 网上关于树链剖分的代码线段树大多都是以结构体形式写的:

struct Tree{
    int left ,right;
    int t ,lazy;
}tree[N<<2];

难道这样更快?不懂(可能这个问题我永远也不会搞懂

 

模板题:HDU 3966 Aragorn's Story

区间修改,单点查询

这个我线段树没用结构体写也过了

#include<bits/stdc++.h>

#define fi first
#define se second
#define rep( i ,x ,y ) for( int i= x; i<= y ;i++ )
#define reb( i ,y ,x ) for( int i= y; i>= x ;i-- )
#define mem( a ,x ) memset( a ,x ,sizeof(a))
#define lson  pos<<1 ,l ,m
#define rson  pos<<1|1 ,m+1 ,r
using namespace std;

typedef long long ll;
typedef long double ld;
typedef pair<int ,int> pii;
typedef pair<ll ,ll> pll;
typedef pair<string ,int> psi;

const int inf = 0x3f3f3f3f;
const int N = 50005;
const int M = 1000005;

struct Edge{
    int next ,to;
}edge[N<<2];
int sz[N] ,top[N] ,son[N] ,dep[N];
int fa[N] ,dfn[N] ,rnk[N];
int m ,n ,q ,tot = 0 ,cnt = 0 ,k;
int head[N] ;
ll val[N<<2] ,tree[N<<4] ,lazy[N<<4];

//线段树部分
void build( int pos ,int l ,int r ){
    if( l== r){
        tree[pos] = val[ rnk[l] ];
        return;
    }

    int m = (l+r)>>1;
    build ( rson );
    build ( lson );
    tree[pos] = tree[pos<<1]+tree[pos<<1|1];
    return;
}

void push_down( int pos ){
    int lc = pos<<1 ,rc = pos<<1|1 ;
    tree[lc] += lazy[pos];
    tree[rc] += lazy[pos];
    lazy[lc] += lazy[pos];
    lazy[rc] += lazy[pos];
    lazy[pos] = 0;
    return;
}

void updata( int pos ,int  l, int r ,int L ,int R ,int k ){
    //cout<<" updata "<<l<<" "<<r<<" "<<L<<" "<<R <<" "<<k<<endl;
    if( L<=l && r<=R ){
        lazy[pos] += k;
        tree[pos] += k;
        return;
    }

    int m = ( l+r )>>1;
    if( L <= m )updata( lson ,L ,R ,k);
    if( R > m )updata( rson ,L ,R ,k);
    tree[pos] = tree[pos>>1] + tree[pos>>1|1];
    return;
}

ll query( int pos ,int l ,int r ,int L ,int R ){
    //cout<<" query "<<l<<" "<<r<<" " <<L <<" "<<R<<endl;
    if( L<=l && r <= R ) return tree[pos];

    ll ans=0;
    push_down( pos );

    int m =( l+r )>>1;
    if( L <=m )ans += query( lson ,L ,R );
    if( R > m )ans += query( rson ,L ,R );
    return ans;
}

//树链剖分部分 --dfs1 ,dfs2 ,query ,updata
void dfs1( int u ,int rt ,int d ){
    //cout<<" dfs1 "<<u<<" "<<rt<<" dep "<<d<<endl;
    dep[u] = d;
    fa[u] = rt;
    sz[u] = 1;

    for( int i = head[u]; i ;i = edge[i].next ){
         int v =edge[i] .to;
         if( v == fa[u] )continue;
         dfs1( v ,u ,d+1 );
         sz[u] += sz[v];
         if( son[u] == 0 || sz[v] >sz[ son[u]]){
             son[u] = v;
         }
    }
    return ;
}

void dfs2( int u ,int t ){
    // u : now t :top
    //cout<<" dfs2 "<<u<<" "<<t<<" son "<<son[u]<<endl;
    top[u] = t;
    dfn[u] = ++cnt;
    rnk[cnt] = u;

    if( !son[u] ){
        return ;
    }
    dfs2( son[u] ,t );
    for( int i = head[u]; i ; i = edge[i].next ){
        int v=  edge[i].to;
        if( v == son[u] || v == fa[u] )continue;
        dfs2( v, v );
    }
}

ll query_path( int x ,int y){
   ll ans = 0;
   int fx = top[x] ,fy = top[y];
   //cout<<" query_p "<<x <<" "<<y<<" "<<fx<<" "<<fy<<endl;
   while( fx != fy ){
          if(dep[fx] >= dep[fy] ){
                ans += query( 1 ,1 ,n ,dfn[fx] ,dfn[x] );
                x = fa[fx] ;
          }  else {
                ans += query( 1 ,1 ,n ,dfn[fy] ,dfn[y] );
                y = fa[fy];
          }
          fx = top[x] ,fy = top[y];
   }

   if(x != y ){
           if( dfn[x] < dfn[y] ){
               ans += query(1 ,1 ,n,dfn[x] ,dfn[y] );
           } else {
               ans += query(1 ,1 ,n,dfn[y] ,dfn[x] );
           }
   } else ans += query( 1 ,1 ,n,dfn[x] ,dfn[y] );
   return ans;
}

void updata_path( int x ,int y ,int z ){
    //cout<<x<<y<<z<<endl;
    int fx = top[x] ,fy = top[y];
    //cout<<" updata_p "<<x <<" "<<y<<" "<<fx<<" "<<fy<<endl;
    while( fx != fy ){
        if( dep[fx] >= dep[fy] ){
            updata( 1 ,1 ,n ,dfn[fx] ,dfn[x] ,z);
            x = fa[fx];
        } else {
            updata( 1 ,1 ,n ,dfn[fy] ,dfn[y] ,z);
            y = fa[fy];
        }
        fx = top[x] ,fy = top[y];
    }

    if( x!=y ){
        if( dfn[x] < dfn[y] )updata( 1 ,1 ,n ,dfn[x] ,dfn[y] ,z );
        else updata( 1 ,1 ,n ,dfn[y] ,dfn[x] ,z );
    } else updata( 1 ,1 , n, dfn[x] ,dfn[y] ,z);
}


int main( ){
    while( ~ scanf("%d%d%d" ,&n ,&m ,&q) ){
        cnt = tot = 0;
        mem( son ,0 );
        mem( tree ,0);
        mem( lazy ,0);

        rep( i ,1 ,n )scanf("%lld" ,&val[i] ) ,head[i] = edge[i].next = 0;
        int l ,r;
        while( m-- ){
            scanf("%d%d" ,&l ,&r );
            edge[++tot].next = head[l];
            edge[tot].to = r;
            head[l] = tot;

            edge[++tot].next = head[r];
            edge[tot].to = l;
            head[r] = tot;
        }

        dfs1( 1 ,1 ,0 );
        dfs2( 1 ,1 );
        build(1, 1 ,n );
        char op;
        while ( q--){
            //cout<<q<<endl;

            cin>>op;
            if( op == 'I' ){
                scanf("%d%d%d" ,&l ,&r ,&k);
                updata_path( l ,r ,k );
            }
            if( op == 'Q' ){
                scanf("%d" ,&l );
                printf("%lld\n" ,query(1 ,1 ,n ,dfn[ l ] ,dfn[ l ] ) );
            }
            if( op == 'D' ){
                scanf("%d%d%d" ,&l ,&r ,&k);
                //cout<<l<<" "<<r<<" "<<k<<endl;
                updata_path( l ,r ,-k );
            }
        }
    }
    return 0;
}
posted @ 2019-08-04 15:03  易如鱼  阅读(208)  评论(0编辑  收藏  举报