題目描述
題目大意:一個圖,動態加邊。保證不會出現環,問你一條邊被多少條路徑經過。點數和詢問數不超過10^5.
思路
LCT維護子樹大小。
每個LCT上的點,維護其全子樹大小sum_siz,虛子樹(虛邊連出去的子樹)大小siz。
一個點本身的大小算進siz裏,在Splay過程中維護sum_siz,即sum_siz=son[0]->sum_siz+son[1]->sum_siz+siz。對於全子樹大小的維護場合跟一般的Up是一樣的。
對於siz的維護,在改變邊的虛實和建立虛邊時維護,就是在Access和Link時維護。當Access砍掉右兒子的時候siz+=son[1]->sum_siz,接上x時siz-=x->sum_siz。
Link的時候要特別注意,如果直接Evert(x),修改y的siz是不行的。因爲如果y不是根的話,y在原樹裏的祖先的sum_siz也是要維護的。這樣不方便,我們不妨將y也Evert到它所在的原樹的根,再令y->siz+=x->sum_siz,就沒有問題了。
維護子樹的操作大概就是這樣,根據我個人的幼稚理解,我們要拿一個點的子樹信息,只有當其處於原樹的根(Evert後)再Splay到根或其所在的Splay所維護的鏈上的最深處(Access後)才能得到正確的答案。
因爲我們維護sum_siz是在Splay上維護的,我們只能拿到其Splay上左右子樹的全子樹及虛子樹信息,只有當前點爲原樹的根且爲Splay的根時才能保證其全子樹信息與原樹子樹信息一致。同理,當一個點爲一條從根往下的鏈的末端時,它的虛子樹信息就是原子樹信息。(然而這題將siz寫成sum_siz交了居然A了??難道是我理解錯了??)
總之,在拿全子樹信息(或實邊信息)時要Splay,其餘情況不用。
胡扯了那麼久,其實題解就一句話。容易發現,一條邊的答案就是邊兩側的點數的乘積。這個用子樹大小就能算出來。
類似的,用這種方法可以維護子樹可加減的其他信息。(比如異或和之類的)
代碼
#include <bits/stdc++.h>
#define maxn 100010
using namespace std;
int n, q, cnt;
char s[5];
struct Tnode{
Tnode *son[2], *fa;
int parent, siz, sum_siz;
bool rev;
int Get_d(){return fa->son[1] == this;}
void Connect(Tnode *now, int d){(son[d] = now)->fa = this;}
void Up(){
sum_siz = siz;
if(son[0]) sum_siz += son[0]->sum_siz;
if(son[1]) sum_siz += son[1]->sum_siz;
}
void Down(){
if(rev){
swap(son[0], son[1]);
if(son[0]) son[0]->rev ^= 1;
if(son[1]) son[1]->rev ^= 1;
rev = false;
}
}
}tree[maxn], *Tr[maxn];
Tnode *NewTnode(){
tree[cnt].son[0] = tree[cnt].son[1] = tree[cnt].fa = NULL;
tree[cnt].siz = tree[cnt].sum_siz = 1;
tree[cnt].parent = 0;
tree[cnt].rev = false;
return tree+cnt++;
}
void Zig(Tnode *now){
Tnode *last = now->fa;
int d = now->Get_d();
if(now->son[!d]) last->Connect(now->son[!d], d);
else last->son[d] = NULL;
if(last->fa) last->fa->Connect(now, last->Get_d());
else now->fa = NULL;
now->Connect(last, !d);
now->parent = last->parent;
last->parent = 0;
last->Up();
}
void Splay(Tnode *now){
Tnode *last;
while(now->fa){
last = now->fa;
if(last->fa) last->fa->Down();
last->Down(); now->Down();
if(last->fa) (now->Get_d() ^ last->Get_d()) ? Zig(now) : Zig(last);
Zig(now);
}
if(!now->fa) now->Down();
now->Up();
}
void Change(int x){
Splay(Tr[x]);
if(Tr[x]->son[1]){
Tr[x]->siz += Tr[x]->son[1]->sum_siz;
Tr[x]->son[1]->fa = NULL;
Tr[x]->son[1]->parent = x;
Tr[x]->son[1] = NULL;
Tr[x]->Up();
}
}
void Access(int x){
Change(x);
int y = Tr[x]->parent;
for(; y; x = y, y = Tr[x]->parent){
Change(y);
Tr[y]->Connect(Tr[x], 1);
Tr[y]->siz -= Tr[x]->sum_siz;
Tr[y]->Up();
Tr[x]->parent = 0;
}
}
void Evert(int x){
Access(x);
Splay(Tr[x]);
Tr[x]->rev ^= 1;
}
void Link(int x, int y){
Evert(x);
Evert(y);
Tr[x]->parent = y;
Tr[y]->siz += Tr[x]->sum_siz;
}
long long Query(int x, int y){
Evert(x);
Access(y);
Splay(Tr[x]);
return 1LL * Tr[y]->siz * (Tr[x]->sum_siz - Tr[y]->siz);
}
int main(){
scanf("%d%d", &n, &q);
for(int i = 1; i <= n; i++) Tr[i] = NewTnode();
int x, y;
while(q --){
scanf("%s%d%d", s, &x, &y);
if(s[0] == 'A') Link(x, y);
else printf("%lld\n", Query(x, y));
}
return 0;
}
猶如晝去夜來