有些題目,在要求支持link-cut之外,還會在線詢問某個子樹的信息。LCT可以通過維護虛邊信息完成這個操作。
對於LCT上每個節點,維護兩個兩sz和si,后者維護該點所有虛兒子的信息,前者維護該點的所有信息和。
那么顯然有:$si[x]=\sum sz[pson]$,$sz[x]=sz[lson]+sz[rson]+si[x]+v[x]$。
其中pson是虛兒子,lson,rson是LCT上的實兒子,v是節點本身的信息。
那么,考慮在哪些操作下需要更新這兩個值。
1.access 每次將舊虛兒子從si中減去,加入新虛兒子的貢獻。
2.link f[x]=y后,將x的信息計入si[y]。
別的操作要注意pushup。
這樣我們維護了sz和si這兩個變量,當想查詢y的子樹的信息和的時候,只需access(y),splay(y)后查詢si[y]即可。
[BZOJ4530][BJOI2014]大融合
n個點,n-1次加邊,最后形成一棵樹。加邊過程中會詢問某條邊現在被多少對點(x,y)之間的路徑經過。
顯然是link的過程中查詢子樹大小,最后詢問的就是(整個連通塊的大小-x子樹的大小)*(x子樹的大小)。
按上面的方法維護子樹大小即可。
1 #include<cstdio> 2 #include<algorithm> 3 #define lc ch[x][0] 4 #define rc ch[x][1] 5 #define rep(i,l,r) for (int i=(l); i<=(r); i++) 6 typedef long long ll; 7 using namespace std; 8 9 const int N=100010; 10 char op; 11 int n,Q,u,v,sz[N],f[N],ch[N][2],rev[N],si[N]; 12 13 bool isrt(int x){ return (!f[x]) || (ch[f[x]][0]!=x && ch[f[x]][1]!=x); } 14 void upd(int x){ sz[x]=sz[lc]+sz[rc]+si[x]+1; } 15 void put(int x){ swap(lc,rc); rev[x]^=1; } 16 void push(int x){ if (rev[x]) put(lc),put(rc),rev[x]=0; } 17 void pd(int x){ if (!isrt(x)) pd(f[x]); push(x); } 18 19 void rot(int x){ 20 int y=f[x],z=f[y],w=ch[y][1]==x; 21 if (!isrt(y)) ch[z][ch[z][1]==y]=x; 22 f[x]=z; f[y]=x; f[ch[x][w^1]]=y; 23 ch[y][w]=ch[x][w^1]; ch[x][w^1]=y; upd(y); 24 } 25 26 void splay(int x){ 27 pd(x); 28 while (!isrt(x)){ 29 int y=f[x],z=f[y]; 30 if (!isrt(y)) (ch[y][1]==x)^(ch[z][1]==y) ? rot(x) : rot(y); 31 rot(x); 32 } 33 upd(x); 34 } 35 36 void access(int x){ 37 for (int y=0; x; y=x,x=f[x]) 38 splay(x),si[x]=si[x]+sz[rc]-sz[y],rc=y,upd(x); 39 } 40 41 void mkrt(int x){ access(x); splay(x); put(x); } 42 43 void link(int x,int y){ 44 mkrt(x); access(y); splay(y); 45 f[x]=y; si[y]+=sz[x]; upd(y); 46 } 47 48 ll que(int x,int y){ mkrt(x); access(y); splay(y); return 1ll*(sz[y]-sz[x])*sz[x]; } 49 50 int main(){ 51 scanf("%d%d",&n,&Q); 52 rep(i,1,n) sz[i]=1; 53 while (Q--){ 54 scanf(" %c%d%d",&op,&u,&v); 55 if (op=='A') link(u,v); else printf("%lld\n",que(u,v)); 56 } 57 return 0; 58 }
[BZOJ3510]首都
link的同時詢問某個連通塊的重心,以及所有連通塊重心編號的異或和。
仍然考慮用LCT維護子樹大小。每次link兩個連通塊時,先作普通的link操作,然后求出新的連通塊的重心。
顯然新重心一定在原來兩個連通塊重心的連線上,split出這條鏈后在鏈上二分。
用ls,rs分別維護這條鏈兩端已被二分排除的部分的大小,每次如果發現當前點合法則更新答案,否則往大的子樹那邊跳。
記得pushdown。多splay以保證復雜度。
1 #include<cstdio> 2 #include<algorithm> 3 #define lc ch[x][0] 4 #define rc ch[x][1] 5 #define rep(i,l,r) for (int i=(l); i<=(r); i++) 6 typedef long long ll; 7 using namespace std; 8 9 const int N=100010; 10 char op[10]; 11 int n,Q,ans,x,y,sz[N],fa[N],f[N],ch[N][2],rev[N],si[N]; 12 13 int get(int x){ return fa[x]==x ? x : fa[x]=get(fa[x]); } 14 bool isrt(int x){ return (!f[x]) || (ch[f[x]][0]!=x && ch[f[x]][1]!=x); } 15 void upd(int x){ sz[x]=sz[lc]+sz[rc]+si[x]+1; } 16 void put(int x){ swap(lc,rc); rev[x]^=1; } 17 void push(int x){ if (rev[x]) put(lc),put(rc),rev[x]=0; } 18 void pd(int x){ if (!isrt(x)) pd(f[x]); push(x); } 19 20 void rot(int x){ 21 int y=f[x],z=f[y],w=ch[y][1]==x; 22 if (!isrt(y)) ch[z][ch[z][1]==y]=x; 23 f[x]=z; f[y]=x; f[ch[x][w^1]]=y; 24 ch[y][w]=ch[x][w^1]; ch[x][w^1]=y; upd(y); 25 } 26 27 void splay(int x){ 28 pd(x); 29 while (!isrt(x)){ 30 int y=f[x],z=f[y]; 31 if (!isrt(y)) (ch[y][1]==x)^(ch[z][1]==y) ? rot(x) : rot(y); 32 rot(x); 33 } 34 upd(x); 35 } 36 37 void access(int x){ 38 for (int y=0; x; y=x,x=f[x]) 39 splay(x),si[x]=si[x]+sz[rc]-sz[y],rc=y,upd(x); 40 } 41 42 void mkrt(int x){ access(x); splay(x); put(x); } 43 44 void split(int x,int y){ mkrt(x); access(y); splay(y); } 45 46 void link(int x,int y){ 47 mkrt(x); mkrt(y); f[x]=y; si[y]+=sz[x]; upd(y); 48 x=get(x); y=get(y); split(x,y); 49 int s=n+1,now=y,ls=0,rs=0,lim=sz[y]/2; 50 while (now){ 51 push(now); int s1=ls+sz[ch[now][0]],s2=rs+sz[ch[now][1]]; 52 if (s1<=lim && s2<=lim) s=min(s,now); 53 if (s1>s2) rs+=sz[ch[now][1]]+si[now]+1,now=ch[now][0]; 54 else ls+=sz[ch[now][0]]+si[now]+1,now=ch[now][1]; 55 } 56 splay(s); fa[x]=fa[y]=fa[s]=s; ans=ans^x^y^s; 57 } 58 59 int main(){ 60 scanf("%d%d",&n,&Q); 61 rep(i,1,n) ans^=i,sz[i]=1,fa[i]=i; 62 while (Q--){ 63 scanf("%s",op); 64 if (op[0]=='X') printf("%d\n",ans); 65 else if (op[0]=='Q') scanf("%d",&x),printf("%d\n",get(x)); 66 else scanf("%d%d",&x,&y),link(x,y); 67 } 68 return 0; 69 }
[UOJ#207]共價大爺游長沙
支持:往詢問集合中添加刪除路徑,link-cut,詢問某條邊是否被集合中的所有路徑經過。
一個顯然的結論是,一條邊(x,y)被所有路徑經過,當且僅當整棵樹以x為根時,所有路徑恰有一個端點在y子樹內。
於是我們將所有路徑的兩端都加上一個隨機權值(不同路徑用不同權值,同一路徑兩端用同一個權值),再詢問y子樹內所有點的權值異或和。
若等於所有路徑隨機權值的異或和則說明所有路徑均恰有一端點在子樹內。
隨機種子要帶時間戳否則會被Hack數據卡掉。
1 #include<cstdio> 2 #include<ctime> 3 #include<algorithm> 4 #define lc ch[x][0] 5 #define rc ch[x][1] 6 #define rep(i,l,r) for (int i=(l); i<=(r); i++) 7 typedef long long ll; 8 using namespace std; 9 10 const int N=100010; 11 int op; 12 int n,Q,ans,x,y,tot,sz[N],fa[N],f[N],ch[N][2],rev[N],si[N],w[N*3],tx[N*3],ty[N*3]; 13 14 int Rand(){ return (rand()<<15)+rand(); } 15 int get(int x){ return fa[x]==x ? x : fa[x]=get(fa[x]); } 16 bool isrt(int x){ return (!f[x]) || (ch[f[x]][0]!=x && ch[f[x]][1]!=x); } 17 void upd(int x){ sz[x]=sz[lc]^sz[rc]^si[x]; } 18 void put(int x){ swap(lc,rc); rev[x]^=1; } 19 void push(int x){ if (rev[x]) put(lc),put(rc),rev[x]=0; } 20 void pd(int x){ if (!isrt(x)) pd(f[x]); push(x); } 21 22 void rot(int x){ 23 int y=f[x],z=f[y],w=ch[y][1]==x; 24 if (!isrt(y)) ch[z][ch[z][1]==y]=x; 25 f[x]=z; f[y]=x; f[ch[x][w^1]]=y; 26 ch[y][w]=ch[x][w^1]; ch[x][w^1]=y; upd(y); 27 } 28 29 void splay(int x){ 30 pd(x); 31 while (!isrt(x)){ 32 int y=f[x],z=f[y]; 33 if (!isrt(y)) (ch[y][1]==x)^(ch[z][1]==y) ? rot(x) : rot(y); 34 rot(x); 35 } 36 upd(x); 37 } 38 39 void access(int x){ 40 for (int y=0; x; y=x,x=f[x]) 41 splay(x),si[x]=si[x]^sz[rc]^sz[y],rc=y,upd(x); 42 } 43 44 void mkrt(int x){ access(x); splay(x); put(x); } 45 void split(int x,int y){ mkrt(x); access(y); splay(y); } 46 void link(int x,int y){ mkrt(x); access(y); splay(y); f[x]=y; si[y]^=sz[x]; } 47 void cut(int x,int y){ split(x,y); f[x]=ch[y][0]=0; upd(y); } 48 void add(int x,int k){ mkrt(x); sz[x]^=k; si[x]^=k; } 49 50 int main(){ 51 srand(time(NULL)); 52 scanf("%*d%d%d",&n,&Q); 53 rep(i,2,n) scanf("%d%d",&x,&y),link(x,y); 54 while (Q--){ 55 scanf("%d",&op); 56 if (op==1) scanf("%d%d",&x,&y),cut(x,y),scanf("%d%d",&x,&y),link(x,y); 57 if (op==2){ 58 tot++; scanf("%d%d",&tx[tot],&ty[tot]); w[tot]=Rand(); 59 ans^=w[tot]; add(tx[tot],w[tot]); add(ty[tot],w[tot]); 60 } 61 if (op==3) scanf("%d",&x),ans^=w[x],add(tx[x],w[x]),add(ty[x],w[x]); 62 if (op==4) scanf("%d%d",&x,&y),split(x,y),puts((si[y]==ans)?"YES":"NO"); 63 } 64 return 0; 65 }