1. 程式人生 > 其它 >樹鏈剖分筆記

樹鏈剖分筆記

1.將一棵樹轉化成一個序列

2.樹中路徑轉化成logn段連續區間

重兒子

重邊

重鏈:對於輕兒子,重鏈自它起始

重兒子在它父節點的重鏈上

dfs中優先遍歷重兒子 ,保證重鏈上所有點的編號是連續的

定理:樹中任意一條路徑均可拆分成O(logn)個重鏈(即為logn個連續區間)

#include <iostream>
#include <cstring>
#include <algorithm>
typedef long long LL;

using namespace std;
const int N = 1e5+10,M=2e5+10;
int n,m;
int h[N],w[N],e[M],ne[M],idx;
void add(int a,int b){
    e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
int dep[N],sz[N],son[N],fa[N],p[N];
int dfn[N],wt[N],cnt;

void dfs1(int u,int f,int d){
    dep[u]=d;
    sz[u]=1;
    fa[u]=f;
    for(int i=h[u];~i;i=ne[i]){
        int k=e[i];
        if(k==f)continue;
        dfs1(k,u,d+1);
        sz[u]+=sz[k];
        if(sz[k]>sz[son[u]])son[u]=k;
    }
}
void dfs2(int u,int f){
    p[u]=f;
    dfn[u]=++cnt;
    wt[cnt]=w[u];
    if(!son[u])return ;
    dfs2(son[u],f);
    for(int i=h[u];~i;i=ne[i]){
        int k=e[i];
        if(k==fa[u])continue;
        if(k==son[u])continue;
        dfs2(k,k);
    }
}

struct Node{
    int l,r;
    LL sum,add;
}tr[4*N];
void pu(int u){
    tr[u].sum=tr[u<<1].sum+tr[u<<1|1].sum;
}
void pd(int u){
    Node &root=tr[u],&left=tr[u<<1],&right=tr[u<<1|1];
    if(root.add){
        left.add+=root.add,right.add+=root.add;
        left.sum+=(LL)(left.r-left.l+1)*root.add;
        right.sum+=(LL)(right.r-right.l+1)*root.add;    
        root.add=0;
    }
    
}

void build(int u,int l,int r){
    tr[u]={l,r,0,0};
    if(l==r){
        tr[u].sum=wt[l];
        return ;
    }
    int mid=l+r>>1;
    build(u<<1,l,mid);
    build(u<<1|1,mid+1,r);
    pu(u);
}
void modify(int u,int l,int r,int k){
    if(tr[u].l>=l&&tr[u].r<=r){
        tr[u].sum+=(LL)(tr[u].r-tr[u].l+1)*k;
        tr[u].add+=k;
        return ;
    }
    pd(u);
    int mid=tr[u].l+tr[u].r>>1;
    if(l<=mid)modify(u<<1,l,r,k);
    if(r>mid)modify(u<<1|1,l,r,k);
    pu(u);
}
LL query(int u,int l,int r){
    if(tr[u].l>=l&&tr[u].r<=r)return tr[u].sum;
    pd(u);
    int mid=tr[u].l+tr[u].r>>1;
    LL res=0;
    if(l<=mid)res+=query(u<<1,l,r);
    if(r>mid)res+=query(u<<1|1,l,r);
    return res;
}
void add_path(int u,int v,int k){
    while(p[u]!=p[v]){
        if(dep[p[u]]<dep[p[v]])swap(u,v);
        modify(1,dfn[p[u]],dfn[u],k);
        u=fa[p[u]];
    }
    if(dep[u]<dep[v])swap(u,v);
    modify(1,dfn[v],dfn[u],k);
    
}
void add_tree(int u,int k){
    modify(1,dfn[u],dfn[u]+sz[u]-1,k);
}
LL query_path(int u,int v){
    LL res=0;
    while(p[u]!=p[v]){
        if(dep[p[u]]<dep[p[v]])swap(u,v);
        res+=query(1,dfn[p[u]],dfn[u]);
        u=fa[p[u]];
    }
    if(dep[u]<dep[v])swap(u,v);
    res+=query(1,dfn[v],dfn[u]);
    return res;
}
LL query_tree(int u){
    return query(1,dfn[u],dfn[u]+sz[u]-1);
}
int main()
{
    ios::sync_with_stdio(0);
    memset(h, -1, sizeof h);
    cin>>n;
    for(int i=1;i<=n;i++)cin>>w[i];
    for(int i=1;i<n;i++){
        int a,b;
        cin>>a>>b;
        add(a,b),add(b,a);
    }
    dfs1(1,-1,1);
    dfs2(1,1);
    build(1,1,n);
    cin>>m;
    while(m--){
        int op,u,v,k;
        cin>>op;
        if(op==1){
            cin>>u>>v>>k;
            add_path(u,v,k);
        }
        else if(op==2){
            cin>>u>>k;
            add_tree(u,k);
        }
        else if(op==3){
            cin>>u>>v;
            cout<<query_path(u,v)<<endl;
        }
        else{
            cin>>u;
            cout<<query_tree(u)<<endl;
        }
    }
}