dfs序和树链剖分

dfs序和树链剖分

总结

  • dfs序和树链剖分是把树形结构映射成线性结构,然后通过例如线段树这样的数据结构维护区间信息
  • dfs序是对树上节点进行dfs排列生成新的编号,其优点在于每一棵树/子树的编号是连续的,这样可以方便的进行任意子树的修改
  • 树链剖分是一种以重链/重边作为dfs优先遍历,这样不单单能够对任意子树进行修改,还能对树上两节点之间的简单路径进行方便的修改。后者的操作是每次修改(区间更新)top[]更深的重链,最终使得两个节点在一条重链上,再修改这条重链。
    由于需要修改重链数目有限(不加证明的给出是\(logn\)),所以每次操作复杂度为\(O({logn}^2)\)

基本步骤

  1. 第一次dfs,预处理出树的size, 节点高度,重儿子,父亲节点
  2. 第二次dfs,以重边优先进行dfs,重新编号,同时处理出重链节点的top[]节点(重链头部的节点)
  3. 线段树维护即可

    reference

    blog0

模板题

模板

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define db(x) cout<<"["<<#x<<"]="<<x<<endl
const ll maxn=1e6+10;
ll N,M;
ll seg[maxn],lazy[maxn];
ll a[maxn],b[maxn];
ll dep[maxn],sz[maxn],son[maxn],fa[maxn];//for dfs1
ll top[maxn],idx[maxn];// for dfs2
vector<ll> G[maxn];
ll tot;//用于重编号
ll dfs1(ll now,ll fath,ll depth){
    dep[now]  =depth;
    sz[now] = 1;
    fa[now] = fath;
    ll maxson = -1;
    for(ll i=0;i<G[now].size();i++){
        ll v = G[now][i];
        if(v==fath) continue; //continue 写法不易出错
        sz[now]+=dfs1(v,now,depth+1);
        if(sz[v]>maxson){maxson = sz[v];son[now] = v;}
    }
    return sz[now];
}
void dfs2(ll now,ll topf){
    idx[now] = (++tot);
    b[tot] = a[now];//
    top[now] = topf;
    if(!son[now])return ;
    dfs2(son[now],topf);
    for(ll i=0;i<G[now].size();i++){
        ll v = G[now][i];
        if(!idx[v]) dfs2(v,v);
    }

}
void build(ll root,ll l,ll r){
    if(l==r){
        seg[root] = b[l];
        return ;
    }
    ll mid = (l+r)>>1;
    build(root<<1,l,mid);
    build(root<<1|1,mid+1,r);
    seg[root] = seg[root<<1]+seg[root<<1|1];
}
void push_down(ll root,ll l,ll r){
    if(lazy[root]){
        ll mid = (l+r)>>1;
        seg[root<<1] += (mid-l+1)*lazy[root];
        seg[root<<1|1]+=(r-mid)*lazy[root];
        lazy[root<<1] +=lazy[root];
        lazy[root<<1|1]+=lazy[root];
        lazy[root]=0;
    }
}
void pointAdd(ll root,ll l,ll r,ll pos,ll val){
    if(l==r){
        seg[root]+=val; return ;
    }
    push_down(root,l,r);
    ll mid = (l+r)>>1;
    if(pos<=mid) pointAdd(root<<1,l,mid,pos,val);
    else pointAdd(root<<1|1,mid+1,r,pos,val);
    seg[root] = seg[root<<1]+seg[root<<1|1];
}
void intervalAdd(ll root,ll l,ll r,ll x,ll y,ll val){
    if(x<=l&&r<=y){
        seg[root]+=val*(r-l+1);
        lazy[root]+=val;
        return ;
    }
    push_down(root,l,r);
    ll mid = (l+r)>>1;
    if(x<=mid) intervalAdd(root<<1,l,mid,x,y,val);
    if(y>mid) intervalAdd(root<<1|1,mid+1,r,x,y,val);
    seg[root] = seg[root<<1]+seg[root<<1|1];
}
ll intervalAsk(ll root,ll l, ll r,ll x,ll y){
    if(x<=l&&r<=y){
        return seg[root];
    }
    push_down(root,l,r);
    ll mid = (l+r)>>1;
    ll ans = 0;
    if(x<=mid) ans+=intervalAsk(root<<1,l,mid,x,y);
    if(y>mid) ans+=intervalAsk(root<<1|1,mid+1,r,x,y);
    return ans;
    
}
ll treeSum(ll x,ll y){
    ll ans = 0;
    while(top[x]!=top[y]){
        if(dep[top[x]]<dep[top[y]]) swap(x,y);
        ll t=intervalAsk(1,1,N,idx[top[x]],idx[x]);
        ans+=t;
        x = fa[top[x]];
    }
    if(dep[x]>dep[y]) swap(x,y);
    ans+=intervalAsk(1,1,N,idx[x],idx[y]);
    return ans;
}
int main(){
    scanf("%lld %lld",&N,&M);
    for(ll i=1;i<=N;i++)scanf("%lld",a+i);
    for(ll i=1;i<N;i++){
        ll u,v;
        scanf("%lld %lld",&u,&v);
        G[u].push_back(v);
        G[v].push_back(u);
    }
    dfs1(1,0,1);dfs2(1,1);
    build(1,1,N);
    while(M--){
        ll op,x,val;
        scanf("%lld",&op);
        if(op==1){
            scanf("%lld %lld",&x,&val);
            pointAdd(1,1,N,idx[x],val);
        }
        else if(op==2){
            scanf("%lld %lld",&x,&val);
            intervalAdd(1,1,N,idx[x],idx[x]+sz[x]-1,val);
        }
        else{
            scanf("%lld",&x);
            printf("%lld\n",treeSum(1,x));
        }

    }
}

猜你喜欢

转载自www.cnblogs.com/fridayfang/p/11097151.html