【bzoj4530】[Bjoi2014]大融合 LCT維護子樹信息


題目描述

小強要在N個孤立的星球上建立起一套通信系統。這套通信系統就是連接N個點的一個樹。
這個樹的邊是一條一條添加上去的。在某個時刻,一條邊的負載就是它所在的當前能夠聯通的樹上路過它的簡單路徑的數量。
例如,在上圖中,現在一共有了5條邊。其中,(3,8)這條邊的負載是6,因為有六條簡單路徑2-3-8,2-3-8-7,3-8,3-8-7,4-3-8,4-3-8-7路過了(3,8)。
現在,你的任務就是隨着邊的添加,動態的回答小強對於某些邊的負載的詢問。

輸入

第一行包含兩個整數N,Q,表示星球的數量和操作的數量。星球從1開始編號。
接下來的Q行,每行是如下兩種格式之一:
A x y 表示在x和y之間連一條邊。保證之前x和y是不聯通的。
Q x y 表示詢問(x,y)這條邊上的負載。保證x和y之間有一條邊。
1≤N,Q≤100000

輸出

對每個查詢操作,輸出被查詢的邊的負載。

樣例輸入

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;
}

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM