一棵\(n\)個點的樹,每一個點的初始權值爲\(1\)。對於這棵樹有\(q\)個操做,每一個操做爲如下四種操做之一:ios
+ 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\)的餘數。第一行兩個整數\(n,q\)
接下來\(n-1\)行每行兩個正整數\(u,v\),描述這棵樹
接下來\(q\)行,每行描述一個操做spa
對於每一個/對應的答案輸出一行code
3 2
1 2
2 3
* 1 3 4
/ 1 1blog
4ip
100%的數據保證,\[1\le n,q\le 100000,1\le u,v,u1,v1,u2,v2\le n,0\le c\le 100001\]get
思想:用鏈的思想,把樹剖爲多個伸展樹input
樹與樹之間只保存父關係,不保存子關係。io
樹鏈剖分把樹分紅若干條重鏈,對於每條重鏈,用線段樹來維護信息。利用各線段樹的信息來獲得答案。class
access(u)
:把u到根節點變成一條鏈
u是當前點,v是前驅stream
其實就是一層一層往上爬,每次順帶修改鏈上的兒子
void access(int u){ for(int v=0;u;v=u,u=fa[u]){ splay(u); ch[u][1]=v; pushup(u); } }
makeroot(u)
:把u變成根
access+splay
後,u已是根,可splay的路徑上須要進行父子反向,其餘的沒有影響,所以要進行翻轉
void makeroot(int u){ access(u); splay(u); reverse(u); }
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); }
link(u,v)
:鏈接u,v
把u變成根,這時u沒有父親,就能夠安心鏈接了。再把其父親設爲v,就實現了鏈接。
void link(int a,int b){ makeroot(a); fa[a]=b; }
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]); } } }