1. 程式人生 > >[Link-Cut-Tree][BZOJ2631]Tree

[Link-Cut-Tree][BZOJ2631]Tree

動態樹 play using i++ etc con ont bool 100%

題面:

Description:

一棵\(n\)個點的樹,每個點的初始權值為\(1\)。對於這棵樹有\(q\)個操作,每個操作為以下四種操作之一:

  • + u v c:將\(u\)\(v\)的路徑上的點的權值都加上自然數\(c\)
  • - u1 v1 u2 v2:將樹中原有的邊\(u1-v1\)刪除,加入一條新邊\(u2-v2\),保證操作完之後仍然是一棵樹;
  • * u v c:將\(u\)\(v\)的路徑上的點的權值都乘上自然數\(c\)
  • / u v:詢問\(u\)\(v\)的路徑上的點的權值和,求出答案對於\(51061\)的余數。
Input

第一行兩個整數\(n,q\)
接下來\(n-1\)

行每行兩個正整數\(u,v\),描述這棵樹
接下來\(q\)行,每行描述一個操作

Output

對於每個/對應的答案輸出一行

Sample Input

3 2
1 2
2 3
* 1 3 4
/ 1 1

Sample Output

4

HINT

100%的數據保證,\[1\le n,q\le 100000,1\le u,v,u1,v1,u2,v2\le n,0\le c\le 100001\]

  • 作用:類似於動態樹鏈剖分(可以修改點、邊)
  • 思想:用鏈的思想,把樹剖為多個伸展樹

    樹與樹之間只保存父關系,不保存子關系。

    樹鏈剖分把樹分成若幹條重鏈,對於每條重鏈,用線段樹來維護信息。利用各線段樹的信息來得到答案。

    技術分享圖片

1.access(u):把u到根節點變成一條鏈

技術分享圖片
技術分享圖片
u是當前點,v是前驅

其實就是一層一層往上爬,每次順帶修改鏈上的兒子

void access(int u){
    for(int v=0;u;v=u,u=fa[u]){
        splay(u);
        ch[u][1]=v;
        pushup(u);
    }
}
2.makeroot(u):把u變成根

技術分享圖片
access+splay後,u已經是根,可splay的路徑上需要進行父子反向,其他的沒有影響,因此要進行翻轉

void makeroot(int u){
    access(u);
    splay(u);
    reverse(u);
}
3.cut(u,v):切斷u,v之間的連接

技術分享圖片
我們先makeroot(u)+access(v)+splay(v)

由於u和v同在一棵Splay中且u一定是v的父親,所以Splay中v的左兒子一定是u,斷開即可。

void cut(int a,int b){
    makeroot(a);
    access(b);
    splay(b);
    ch[b][0]=0;
    fa[a]=0;
    pushup(b);
}
4.link(u,v):連接u,v

技術分享圖片
把u變成根,這時u沒有父親,就可以安心連接了。再把其父親設為v,就實現了連接。

void link(int a,int b){
    makeroot(a);
    fa[a]=b;
}
5.isconnect(u,v):檢測u,v是否連接

技術分享圖片
我們先makeroot(u)+access(v)+splay(v)

如果u和v不在同一棵LCT中,執行makeroot(u)後,u的父親應該為空(他是根)

除非a和b在同一棵樹中,在access(v)+splay(v)後,u與v應該在同一棵Splay中,既然v是根,那麽u就不是根,即u一定有一個父親存在。

bool isconnect(int a,int b){
    if(a==b) return true;
    makeroot(a);
    access(b);
    splay(b);
    return fa[a];
}

代碼:

註意有多個修改中懶標的特殊處理方式。

#include<iostream>
#include<cstdio>
using namespace std;
int ch[100001][2],fa[100001],siz[100001],lazr[100001],cnt,n,q;
unsigned num[100001],tot[100001],lazp[100001],lazc[100001],mod=51061;
inline unsigned rd(){
    unsigned re=0;
    char ch=getchar();
    while(ch<‘0‘||ch>‘9‘)ch=getchar();
    while(ch>=‘0‘&&ch<=‘9‘){
        re=re*10+ch-‘0‘;
        ch=getchar();
    }
    return re;
}
inline bool isroot(int bt){return ch[fa[bt]][0]!=bt&&ch[fa[bt]][1]!=bt;}
inline int drct(int bt){return ch[fa[bt]][1]==bt;}
inline void pushup(int bt){siz[bt]=siz[ch[bt][0]]+siz[ch[bt][1]]+1;tot[bt]=((tot[ch[bt][0]]+num[bt])%mod+tot[ch[bt][1]])%mod;}
inline void reverse(int bt){swap(ch[bt][0],ch[bt][1]);lazr[bt]^=1;}
inline void add(int bt,unsigned c){num[bt]=(num[bt]+c)%mod;tot[bt]=(tot[bt]+siz[bt]*c)%mod;lazp[bt]=(lazp[bt]+c)%mod;}
inline void times(int bt,unsigned c){num[bt]=(num[bt]*c)%mod;tot[bt]=(tot[bt]*c)%mod;lazc[bt]=(lazc[bt]*c)%mod;lazp[bt]=(lazp[bt]*c)%mod;}
inline void pd(int bt){
    if(lazr[bt]){
        if(ch[bt][0])reverse(ch[bt][0]);
        if(ch[bt][1])reverse(ch[bt][1]);
        lazr[bt]=0;
    }
    if(lazp[bt]){
        if(ch[bt][0])add(ch[bt][0],lazp[bt]);
        if(ch[bt][1])add(ch[bt][1],lazp[bt]);
        lazp[bt]=0;
    }
    if(lazc[bt]!=1){
        if(ch[bt][0])times(ch[bt][0],lazc[bt]);
        if(ch[bt][1])times(ch[bt][1],lazc[bt]);
        lazc[bt]=1;
    }
}
inline void pushdown(int u){
    if(!isroot(u))pushdown(fa[u]);
    pd(u);
}
inline void rotate(int u){
    int f=fa[u],g=fa[f],c=drct(u);
    if(!isroot(f))ch[g][drct(f)]=u;
    fa[u]=g;
    ch[f][c]=ch[u][c^1];
    if(ch[f][c])fa[ch[f][c]]=f;
    ch[u][c^1]=f;
    fa[f]=u;
    pushup(f);
    pushup(u);
}
void splay(int u){
    pushdown(u);
    while(!isroot(u)){
        if(!isroot(fa[u]))rotate(drct(fa[u])==drct(u)?fa[u]:u);
        rotate(u);
    }
}
void access(int u){
    for(int v=0;u;v=u,u=fa[u]){
        splay(u);
        ch[u][1]=v;
        pushup(u);
    }
}
void makeroot(int u){
    access(u);
    splay(u);
    reverse(u);
}
void link(int a,int b){
    makeroot(a);
    fa[a]=b;
}
void cut(int a,int b){
    makeroot(a);
    access(b);
    splay(b);
    ch[b][0]=0;
    fa[a]=0;
    pushup(b);
}
void makeline(int u,int v){
    makeroot(u);
    access(v);
    splay(v);
}
int main(){
    n=rd();
    q=rd();
    for(int i=1;i<=n;i++)lazc[i]=num[i]=tot[i]=siz[i]=1;
    for(int i=1;i<n;i++){
        int u=rd(),v=rd();
        link(u,v);
    }
    makeroot(1);
    for(int i=1;i<=q;i++){
        char cha[5];
        scanf("%s",cha);
        int u=rd(),v=rd();
        if(cha[0]==‘+‘){
            unsigned c=rd();
            makeline(u,v);
            add(v,c);
        }else if(cha[0]==‘-‘){
            int u2=rd(),v2=rd();
            cut(u,v);
            link(u2,v2);
        }else if(cha[0]==‘*‘){
            unsigned c=rd();
            makeline(u,v);
            times(v,c);
        }else if(cha[0]==‘/‘){
            makeline(u,v);
            printf("%u\n",tot[v]);
        }
    }
}

[Link-Cut-Tree][BZOJ2631]Tree