Skip to content

树链剖分之重链剖分 ​

  • 重儿子:儿子中最大size的节点
  • 轻儿子:除重儿子外的儿子
  • 重边:连接重儿子的边
  • 轻边:连接轻儿子的边
  • 重链:重儿子往上走到根节点或者轻儿子的边(亦可以是一个轻儿子)

对一棵树的路径进行大量的操作
树链剖分即将每条重链分出来 $ (O(logN)条) $
将节点按照先重儿子的dfs序排好,用线段树维护每个区间(重链)的信息
$ 总复杂度:O(log^2 N ) $

cpp
struct node{
    ll l,r,add,sum;
} t[N<<2];
ll w[N],siz[N],id[N],nw[N],dep[N],top[N],par[N],son[N];
/*
w[i]是原来的值
siz[i]是树的大小
id[i]是dfs序编号
nw[i]是dfs序为i的值
dep[i]为高度
top[i]为当前i所在的重链的头部
par[i]为父节点
son[i]为重儿子
*/
ll n,m,r,mod;
vector<ll> adj[N];
void dfs1(int u,int fa){//预处理
    par[u]=fa;
    dep[u]=dep[fa]+1;
    siz[u]=1;
    for(auto v:adj[u]){
        if(v==fa)continue;
        dfs1(v,u);
        siz[u]+=siz[v];
        if(siz[son[u]]<siz[v])son[u]=v;
    }
}
int cnt=0;
void dfs2(int u,int t){//dfs序,重链头节点
    id[u]=++cnt;
    nw[cnt]=w[u];
    top[u]=t;
    if(!son[u])return;
    dfs2(son[u],t);
    for(auto v:adj[u]){
        if(v==par[u]||v==son[u])continue;
        dfs2(v,v);
    }
}
void pushup(int p){
    t[p].sum=t[p<<1].sum+t[p<<1|1].sum;
}
void pushdown(int p){
    auto &u=t[p],&l=t[p<<1],&r=t[p<<1|1];
    l.sum+=u.add*(l.r-l.l+1);
    r.sum+=u.add*(r.r-r.l+1);
    l.add+=u.add;
    r.add+=u.add;
    u.add=0;
}
void build(int p,int l,int r){
    t[p]={l,r,0,nw[r]};
    if(l==r)return;
    int mid=l+r>>1;
    build(p<<1,l,mid);
    build(p<<1|1,mid+1,r);
    pushup(p);
}
void modify(int p,int l,int r,int k){
    if(l<=t[p].l&&r>=t[p].r){
        t[p].add+=k;
        t[p].sum+=k*(t[p].r-t[p].l+1);
        return;
    }
    pushdown(p);
    int mid=t[p].l+t[p].r>>1;
    if(l<=mid)modify(p<<1,l,r,k);
    if(r>mid)modify(p<<1|1,l,r,k);
    pushup(p);
}
ll query(int p,int l,int r){
    if(l<=t[p].l&&r>=t[p].r)return t[p].sum;
    pushdown(p);
    ll res=0;
    int mid=t[p].l+t[p].r>>1;
    if(l<=mid)res+=query(p<<1,l,r);
    if(r>mid)res+=query(p<<1|1,l,r);
    return res;
}
void modify_path(int u,int v,int k){
    while(top[u]!=top[v]){
        if(dep[top[u]]<dep[top[v]])swap(u,v);
        modify(1,id[top[u]],id[u],k);
        u=par[top[u]];
    }
    if(dep[u]<dep[v])swap(u,v);
    modify(1,id[v],id[u],k);
}
ll query_path(int u,int v){
    ll res=0;
    while(top[u]!=top[v]){
        if(dep[top[u]]<dep[top[v]])swap(u,v);
        res+=query(1,id[top[u]],id[u]);
        u=par[top[u]];
    }
    if(dep[u]<dep[v])swap(u,v);
    res+=query(1,id[v],id[u]);
    return res;    
}
void modify_tree(int u,int k){
    modify(1,id[u],id[u]+siz[u]-1,k);
}
ll query_tree(int u){
    return query(1,id[u],id[u]+siz[u]-1);
}
void solve(){
    cin>>n>>m>>r>>mod;
    for(int i=1;i<=n;i++)cin>>w[i];
    for(int i=1;i<n;i++){
        int u,v;
        cin>>u>>v;
        adj[u].push_back(v);
        adj[v].push_back(u);
    }
    dfs1(r,0);
    dfs2(r,r);
    build(1,1,n);
    while(m--){
        int op,x,y,z;
        cin>>op>>x;
        if(op==1){
            cin>>y>>z;
            modify_path(x,y,z);
        }
        else if(op==2){
            cin>>y;
            cout<<query_path(x,y)%mod<<"\n";
        }
        else if(op==3){
            cin>>z;
            modify_tree(x,z);
        }
        else{
            cout<<query_tree(x)%mod<<"\n";
        }
    }
}