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

仰天大笑出门去,我辈岂是蓬蒿人!

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