2019-ICPC瀋陽J題Graph

寫這篇博客也是爲了方便自己以後能夠快速回顧LCT,再探索其其他的用途

以後回顧LCT可以參考這篇博客:寫得很清楚

題目如下

Rose would like to find whether they truly love each other.

Rose lets Jack draw a graph of N vertices, and Rose herself writes a set of vertex pair (u​i​​,v​i​​), named S.

Initially the graph has no edge and S is empty.

There are Q operations of 5 kinds, which are listed below:

1.Jack adds edge (u,v) into the graph. It is guaranteed that u and v are unreachable before adding.

2.Jack deletes edge (u,v) from the graph. It is guaranteed that this edge exists before deleting.

3.Rose adds vertex pair (u,v) into S. It is guaranteed that (u,v) is not in S before adding.

4.Rose deletes vertex pair (u,v) from S. It is guaranteed that (u,v) exists before deleting.

5.Ask if they love each other, that is, whether every vertex pair in S is reachable in the graph. In other words, whether for every vertex pair (u,v) in S, u and v are reachable in the graph.

Input
The first line contains an integer T (1≤T≤100) — the number of test cases.

The first line of each test case contains two integers N (3≤N≤100000), Q (3≤Q≤300000) — the number of vertices, the number of operations.

The next Q lines describe the operations, where the i-th line contains three integers Type, u, v (u<v) for the first four operations and one integer Type for the last (fifth) kind of operation.

It is guaranteed that ∑N≤300000 and ∑Q≤500000.

Output
For each operation of the fifth kind, output “YES” or “NO” in one line to indicate the answer, with the same order as the input.

Sample Input
1
5 10
5
3 2 5
4 2 5
1 4 5
2 4 5
5
1 1 2
3 1 5
4 1 5
2 1 2

Sample Output
YES
YES
可以發現題目中的操作涉及刪邊,加邊,存在多個連通塊,更重要的是,由於每次加邊保證兩個點不連通所以所有連通塊都滿足樹的形態。所以我們很快就想到可能可以用到LCT來解決這個問題

判斷所有點對是否都聯通有個巧妙的方法:每次添加一個點對(u,v)到集合S中時, 將這組點對隨機一個值Value,可以用二維map存,並且將這兩個點的權值都異或上Value,這樣如果一個連通塊內所有點的抑或和爲0,即連通塊內部包含的都是S中完整的點對。如果每個連通塊的抑或和都爲0,那麼S中所有的點對都聯通。

對於前四個操作,只要維護連通塊的抑或和即可,同時維護一共有多少個連通塊抑或和不爲零

這裏涉及到LCT維護子樹信息的操作(當然這題只要維護整顆樹的信息)對於點x,用一個sum[x]維護子樹抑或和(包括x),other[x]維護x所有虛邊連接的點的抑或和(不包括x),這樣不光對本題,對於查詢子樹信息的題,例如要查詢y的子樹信息,只需要access(y),other[y]中便記錄了所有子樹信息(不包括y),就成功了

可惜我的代碼習慣不好,還是顯得很冗長,不過懶得改了<_>


#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <ctime>
#include <map> 

using namespace std;

const int Maxn = 100010;

int ch[Maxn][2], fa[Maxn], rev[Maxn], Sum[Maxn], Cnt, Val[Maxn], Other[Maxn];

bool isroot(int x){return ch[fa[x]][0] != x && ch[fa[x]][1] != x;}

void pushup(int h){
	Sum[h] = Val[h];
	Sum[h] ^= Other[h];
	
	if (ch[h][0]) Sum[h] ^= Sum[ch[h][0]];
	if (ch[h][1]) Sum[h] ^= Sum[ch[h][1]];
}

void pushdown(int h){
	if(!rev[h]) return;

	rev[h] = 0;
	swap(ch[h][0], ch[h][1]);

	if(ch[h][0]) rev[ch[h][0]] ^= 1;
	if(ch[h][1]) rev[ch[h][1]] ^= 1;
}

void PUSHDOWN(int h){
	if(!isroot(h)) PUSHDOWN(fa[h]);
	pushdown(h);
}

void rotate(int h){
	int father = fa[h], grandfa = fa[father];
	bool w = (ch[father][1] == h);

	fa[ch[h][w^1]] = father;
	ch[father][w] = ch[h][w^1];

	fa[father] = h;
	ch[h][w^1] = father;

	fa[h] = grandfa;
	if(ch[grandfa][0] == father) ch[grandfa][0] = h;
	else if(ch[grandfa][1] == father) ch[grandfa][1] = h;

	pushup(father), pushup(h);
}

bool pd (int x) {return ch[fa[x]][0] == x;}

void splay (int h) {
	PUSHDOWN(h);

	while (!isroot(h)) {
		if(!isroot(fa[h]) && pd(fa[h]) == pd(h)) rotate(fa[h]);
		rotate(h);
	}
}

void access(int h){
	int y = 0;

	while (h) {
		splay(h);
		
		Other[h] ^= Sum[ch[h][1]];//維護子樹信息只需要在access和link操作多添幾句
		Other[h] ^= Sum[y];
		
		ch[h][1] = y;
		pushup(h);

		y = h;
		h = fa[h];
	}
}

void makeroot (int h) {
	access(h);
	splay(h);
	pushup(h);
	rev[h] ^= 1;
}

int findroot (int x) {
	access(x);
	splay(x);

	while(ch[x][0])	x = ch[x][0];
	return x;
}

void link (int x,int y) {
	makeroot(x);
	makeroot(y);
	int Pre = (Sum[x] != 0) + (Sum[y] != 0);
	fa[x] = y;
	Other[y] ^= Sum[x];
	pushup(y);
	int Next = (Sum[y] != 0);
	Cnt += Next - Pre;//用變化後的數量減去變化前的數量,下面幾個一樣的地方同理
}

void cut (int x,int y) {
	makeroot(x);
	int Pre = (Sum[x] != 0);
	
	access(y);
	splay(y);
	fa[x] = ch[y][0] = 0;
	pushup(y);
	int Next = (Sum[x] != 0) + (Sum[y] != 0);
	Cnt += Next - Pre;
}

map<int, map<int, int> > q;

void Init(int N) {
	q.clear();
	Cnt = 0;
	for (int i = 1; i <= N; ++i) Val[i] = Sum[i] = fa[i] = ch[i][0] = ch[i][1] = Other[i] = 0;
}

void Add(int x, int Value) {
	int Pre = (Sum[x] != 0);
	Val[x] ^= Value; pushup(x);
	
	int Next = (Sum[x] != 0);
	Cnt += Next - Pre;
}

int main() {
	srand(time(NULL));
	int T;
	scanf ("%d", &T);
	while (T--) {
		
		int N, Q;
		scanf ("%d%d", &N, &Q);
		Init(N);
		
		while (Q--) {
			int th;
			scanf ("%d", &th);
			int x, y;
			if (th != 5) scanf ("%d%d", &x, &y);
				
			if (th == 1) {
				link(x, y);
			}
			
			else if (th == 2) {
				cut (x, y);
			}
			
			else if (th == 3) {
				int Value = rand();
				q[x][y] = q[y][x] = Value;
				makeroot(x);
				Add(x, Value);	
				
				makeroot(y);
				Add(y, Value);
			}
			
			else if (th == 4) {
				int Value = q[x][y];
				q[x][y] = q[y][x] = 0;
				
				makeroot(x);
				Add(x, Value);
				
				makeroot(y);
				Add(y, Value);
			}
			
			else {
				if (Cnt) printf ("NO\n");
				else printf ("YES\n");
			}
		}
	}
	
	return 0;
}

仰天大笑出門去,我輩豈是蓬蒿人!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章