1. 程式人生 > 實用技巧 >樹鏈剖分學習筆記(未完)

樹鏈剖分學習筆記(未完)

樹鏈剖分是在維護靜態樹上關於鏈的問題的好工具,感性的認識,就是把樹問題重新轉換為序列問題合併

而其中分為兩種,一種是長鏈剖分,一種是重鏈剖分。

講講重鏈剖分就是把樹剖分成很多條兩種型別的鏈,一種叫重鏈,一種叫輕鏈

而重鏈由重邊構成,即任意一個父節點與其子樹大小最大的兒子節點的連邊

如圖(From chinhhh)

可以發現,任意一個節點在一條重鏈上(假設葉子節點自身也在一個重鏈),且任意一條重鏈不相交(由輕鏈連線),所以我們只要維護重鏈就好了

然後最妙的是 通過先走重兒子再走其他兒子的dfs方法 ,我們發現重鏈上的點在dfs序上連續了!!!這讓我們可以用線段樹之類的資料結構來維護重鏈,將問題看做鏈的合併

看一道板子:P3384 【模板】輕重鏈剖分

程式碼如下

#include<bits/stdc++.h>
using namespace std;
int const MAXN=1e6*4+10;
int N,M,R,P,tot=1,cnt;
int H[MAXN],val[MAXN],top[MAXN],dep[MAXN],id[MAXN],fa[MAXN],hson[MAXN],size[MAXN];
int w[MAXN];
bool vis[MAXN];
struct edge{
    int to,next;
}e[MAXN<<1];
struct node{
    int l,r,add,sum;
}p[MAXN<<2];
void add(int be,int to){
    e[++tot].to=be,e[tot].next=H[to],H[to]=tot;
    e[++tot].to=to,e[tot].next=H[be],H[be]=tot;
}
void dfs1(int x,int father,int depth){
    dep[x]=depth,fa[x]=father,size[x]=1;
    int maxn=0;
    for(int i=H[x];i;i=e[i].next){
        int to=e[i].to;if(to==father)continue;
        dfs1(to,x,depth+1);
        size[x]+=size[to];
        if(size[to]>maxn)hson[x]=to,maxn=size[to];
    }
}
void dfs2(int x,int topf){
    id[x]=++cnt,top[x]=topf,w[id[x]]=val[x];
    if(!hson[x])return;
    dfs2(hson[x],topf);
    for(int i=H[x];i;i=e[i].next){
        int to=e[i].to;
        if(to==fa[x] || to==hson[x])continue;
        dfs2(to,to);
    }
}
void build(int x,int l,int r){
    p[x].l=l,p[x].r=r;
    if(l==r){p[x].sum=w[l]%P;return;}
    int mid=(l+r)>>1;
    build(x<<1,l,mid);build(x<<1|1,mid+1,r);
    p[x].sum=(p[x<<1].sum+p[x<<1|1].sum)%P;
    return;
}
void pushdown(int x){
    if(p[x].add){
        p[x<<1].sum=(p[x<<1].sum+(p[x<<1].r-p[x<<1].l+1)*p[x].add)%P;
        p[x<<1|1].sum=(p[x<<1|1].sum+(p[x<<1|1].r-p[x<<1|1].l+1)*p[x].add)%P;
        p[x<<1].add=(p[x<<1].add+p[x].add)%P;
        p[x<<1|1].add=(p[x<<1|1].add+p[x].add)%P;
        p[x].add=0;
    }
}
void add(int x,int l,int r,int val){
    if(l<=p[x].l && r>=p[x].r){
        p[x].sum=(p[x].sum+val*(p[x].r-p[x].l+1))%P;
        p[x].add=(p[x].add+val)%P;
        return;
    }
    pushdown(x);
    int mid=(p[x].l+p[x].r)>>1;
    if(l<=mid)add(x<<1,l,r,val);
    if(r>mid)add(x<<1|1,l,r,val);
    p[x].sum=(p[x<<1].sum+p[x<<1|1].sum)%P;
}
int query(int x,int l,int r){
    if(l<=p[x].l && p[x].r<=r)return p[x].sum%P;
    pushdown(x);
    int mid=(p[x].l+p[x].r)>>1,val=0;
    if(l<=mid)val=(val+query(x<<1,l,r))%P;
    if(r>mid)val=(val+query(x<<1|1,l,r))%P;
    return val;
}
void add_way(int x,int y,int val){
    while(top[x]!=top[y]){
        if(dep[top[y]]>dep[top[x]])swap(x,y);
        add(1,id[top[x]],id[x],val);
        x=fa[top[x]];
    }
    add(1,min(id[x],id[y]),max(id[x],id[y]),val);
}
int query_way(int x,int y){
    int ans=0;
    while(top[x]!=top[y]){
        if(dep[top[y]]>dep[top[x]])swap(x,y);
        ans=(ans+query(1,id[top[x]],id[x]))%P;
        x=fa[top[x]];
    }
    ans=(ans+query(1,min(id[x],id[y]),max(id[x],id[y])))%P;
    return ans;
}
void add_ctree(int x,int val){
    add(1,id[x],id[x]+size[x]-1,val);
}
int query_ctree(int x){
    return query(1,id[x],id[x]+size[x]-1);
}
int main(){
    scanf("%d%d%d%d",&N,&M,&R,&P);
    for(int i=1;i<=N;i++)scanf("%d",&val[i]);
    for(int i=1;i<=N-1;i++){
        int x,y;scanf("%d%d",&x,&y);add(x,y);
    }
    dfs1(R,R,1);
    dfs2(R,R);
    build(1,1,N);
    for(int i=1;i<=M;++i){
        int op,x,y,z;scanf("%d",&op);
        if(op==1){
            scanf("%d%d%d",&x,&y,&z);
            add_way(x,y,z);
        }else if(op==2){
            scanf("%d%d",&x,&y);
            printf("%d\n",query_way(x,y));
        }else if(op==3){
            scanf("%d%d",&x,&z);
            add_ctree(x,z);
        }else if(op==4){
            scanf("%d",&x);
            printf("%d\n",query_ctree(x));
        }
    }
    return 0;
}