【BZOJ2049】【SDOI2008】洞穴勘测

【题目链接】

【思路要点】

  • LinkCutTree模板题,支持维护动态森林的联通性,通过询问所在子树的根部来实现。
  • 时间复杂度\(O(MLogN)\)。

【代码】

#include<bits/stdc++.h>
using namespace std;
#define MAXN	10005
struct LinkCutTree {
	struct Node {
		bool rev;
		int father, up, child[2];
	};
	Node a[MAXN];
	int size, n;
	void init(int x) {
		n = x;
		for (int i = 1; i <= n; i++) {
			size++;
			a[size].father = 0;
			a[size].up = 0;
			a[size].child[0] = 0;
			a[size].child[1] = 0;
			a[size].rev = false;
		}
	}
	void pushdown(int x) {
		if (a[x].rev == false) return;
		swap(a[x].child[0], a[x].child[1]);
		a[a[x].child[0]].rev ^= true;
		a[a[x].child[1]].rev ^= true;
		a[x].rev = false;
	}
	bool get(int x) {
		pushdown(a[x].father);
		return x == a[a[x].father].child[1];
	}
	void rotate(int x) {
		int f = a[x].father;
		pushdown(f); pushdown(x);
		int g = a[f].father, tmp = get(x);
		a[x].up = a[f].up;
		a[f].up = 0;
		a[f].child[tmp] = a[x].child[tmp ^ 1];
		a[a[x].child[tmp ^ 1]].father = f;
		a[x].child[tmp ^ 1] = f;
		a[f].father = x;
		a[x].father = g;
		if (g) a[g].child[a[g].child[1] == f] = x;
	}
	void splay(int x) {
		pushdown(x);
		for (int f = a[x].father; (f = a[x].father); rotate(x))
			if (a[f].father) rotate((get(f) == get(x)) ? f : x);
	}
	void access(int x) {
		splay(x);
		a[a[x].child[1]].up = x;
		a[a[x].child[1]].father = 0;
		a[x].child[1] = 0;
		while (a[x].up) {
			int tmp = a[x].up;
			splay(tmp);
			a[a[tmp].child[1]].up = tmp;
			a[a[tmp].child[1]].father = 0;
			a[tmp].child[1] = x;
			a[x].father = tmp;
			a[x].up = 0;
			splay(x);
		}
	}
	void reverse(int x) {
		access(x);
		splay(x);
		a[x].rev ^= true;
	}
	void link(int x, int y) {
		access(x);
		splay(x);
		reverse(y);
		a[x].child[1] = y;
		a[y].father = x;
	}
	void cut(int x, int y) {
		reverse(x);
		access(y);
		splay(x);
		a[x].child[1] = 0;
		a[y].father = 0;
	}
	int root(int x) {
		access(x);
		splay(x);
		int now = x;
		pushdown(x);
		while (a[now].child[0]) {
			now = a[now].child[0];
			pushdown(now);
		}
		return now;
	}
};
LinkCutTree T;
int main() {
	int n, m;
	scanf("%d%d", &n, &m);
	T.init(n);
	for (int i = 1; i <= m; i++) {
		char opt[10];
		int x, y;
		scanf("%s%d%d", opt, &x, &y);
		if (opt[0] == 'C') T.link(x, y);
		if (opt[0] == 'D') T.cut(x, y);
		if (opt[0] == 'Q') { 
			if (T.root(x) == T.root(y)) printf("Yes\n");
			else printf("No\n");
		}
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值