題目描述

輸入
輸出
樣例輸入
8 6
A 2 3
A 3 4
A 3 8
A 8 7
A 6 5
Q 3 8
樣例輸出
6
題解
LCT維護子樹信息
學了大神的LCT維護子樹信息的方式,覺得還算好理解,於是自己yy了這道題。
我們知道,在LCT中的Splay Tree中,access某個點並splay到根,那么它的實兒子記錄的信息是這條鏈的信息,並不是我們想要的子樹信息。
而所有實兒子和虛兒子的信息才是我們想要求的子樹信息。
但是由於虛兒子“兒子認爹,爹不認兒子”的性質,無法在pushup的時候上傳信息。
事實上,我們注意到,對於Splay Tree的所有基本操作,除了access和link以外,都不會對虛兒子的信息進行修改。
那么我們每次在添加虛兒子時,順便把虛兒子的信息也記錄到父親節點中。
這樣我們每次調用一個節點時,將它Splay Tree中實兒子的信息,加上它自身的虛兒子的信息,就是我們想要的子樹信息。
於是我們對於每個節點記錄兩個信息:它的總信息和它虛兒子的信息,pushup時更新x的總信息為:x實兒子的總信息+x虛兒子的信息+x本身的信息。
按照這種方法我們來思考這道題,可以發現所求的答案就是一條邊兩端點的子樹大小乘積,我們把某一個端點定為整棵樹的根,可以知道整棵樹的大小,而根據另一個節點可以知道一個子樹的大小,相減即為另一個子樹的大小。
具體的實現:
access操作中割斷了實邊c[1][x],該邊變為了虛邊,所以應該加到x的虛兒子信息中,加入了實邊t,該邊不再是虛邊,所以應從x的虛兒子信息中減去。
link操作中為了在加入x時同時更新y的信息,需要makeroot(x),makeroot(y),然后連x->y的虛邊(實際上只需要access(y)和splay(y))。
其余的操作,和普通的LCT沒有任何區別。
代碼中需要注意的是,sum[x]存的是總信息(子樹大小),si[x]存的是虛兒子信息(子樹除了鏈以外的大小),不要弄混。
#include <cstdio> #include <algorithm> #define N 100010 using namespace std; int fa[N] , c[2][N] , si[N] , sum[N] , rev[N]; char str[5]; void pushup(int x) { sum[x] = sum[c[0][x]] + sum[c[1][x]] + si[x] + 1; } void pushdown(int x) { if(rev[x]) { int l = c[0][x] , r = c[1][x]; swap(c[0][l] , c[1][l]) , swap(c[0][r] , c[1][r]); rev[l] ^= 1 , rev[r] ^= 1 , rev[x] = 0; } } bool isroot(int x) { return c[0][fa[x]] != x && c[1][fa[x]] != x; } void update(int x) { if(!isroot(x)) update(fa[x]); pushdown(x); } void rotate(int x) { int y = fa[x] , z = fa[y] , l = (c[1][y] == x) , r = l ^ 1; if(!isroot(y)) c[c[1][z] == y][z] = x; fa[x] = z , fa[y] = x , fa[c[r][x]] = y , c[l][y] = c[r][x] , c[r][x] = y; pushup(y) , pushup(x); } void splay(int x) { update(x); while(!isroot(x)) { int y = fa[x] , z = fa[y]; if(!isroot(y)) { if((c[0][y] == x) ^ (c[0][z] == y)) rotate(x); else rotate(y); } rotate(x); } } void access(int x) { int t = 0; while(x) splay(x) , si[x] += sum[c[1][x]] - sum[t] , c[1][x] = t , pushup(x) , t = x , x = fa[x]; } void makeroot(int x) { access(x) , splay(x) , swap(c[0][x] , c[1][x]) , rev[x] = 1; } void split(int x , int y) { makeroot(x) , makeroot(y); } void link(int x , int y) { split(x , y) , fa[x] = y , si[y] += sum[x] , pushup(y); } int main() { int n , m , i , x , y; scanf("%d%d" , &n , &m); for(i = 1 ; i <= n ; i ++ ) sum[i] = 1; while(m -- ) { scanf("%s%d%d" , str , &x , &y); if(str[0] == 'A') link(x , y); else split(x , y) , printf("%lld\n" , (long long)sum[x] * (sum[y] - sum[x])); } return 0; }