只要你跑的够快,锅就追不上你

0%

「LOJ 121」动态图连通性(分治 + 并查集)

题目大意

「LOJ 121」动态图连通性

给定一个 $n$ 个点的图和 $m$ 个操作。图一开始是空的,每次操作可以在图中加上一条边,删去一条边或者询问两个点是否连通。你要对于所有询问求出答案。

数据范围:$n \le 5 \times 10^3, m \le 5 \times 10^5$。

思路分析

不知道这个 $n \le 5 \times 10^3$ 有什么用 \kel。

如果只有加边的话,我们可以直接使用并查集来做。如果带上删边,我们就可以使用按时间分治的技巧。具体地,我们将每条边出现的时间和被删除的时间处理出来,然后在时间的线段树上插入对应的区间。最后,我们 DFS 一遍线段树就可以求出所有答案了。

那么我们需要支持的操作就是连边和撤销上一次连边。发现并不需要可持久化,我们只需要可回退的并查集就行了。对于一次 merge 操作,我们将它的信息记录下来,撤销的时候再还原即可。注意我们不能路径压缩,所以需要启发式合并来优化。时间复杂度 $O(m \log^2 m)$,具体见代码。

代码实现

1
#include <bits/stdc++.h>
2
#define ls (rt << 1)
3
#define rs (ls | 1)
4
#define mid ((l + r) >> 1)
5
using namespace std;
6
7
const int maxn = 5e5, maxm = 1 << 20;
8
int n, m, o[maxn + 3], u[maxn + 3], v[maxn + 3], fa[maxn + 3], sz[maxn + 3];
9
int top, st_u[maxn + 3], st_v[maxn + 3], st_o[maxn + 3], st_s[maxn + 3];
10
bool ans[maxn + 3];
11
vector<int> S[maxm + 3];
12
map<pair<int, int>, int> mp;
13
14
void modify(int rt, int l, int r, int lx, int rx, int x) {
15
	if (l >= lx && r <= rx) {
16
		S[rt].push_back(x);
17
		return;
18
	}
19
	if (lx <= mid) {
20
		modify(ls, l, mid, lx, rx, x);
21
	}
22
	if (rx > mid) {
23
		modify(rs, mid + 1, r, lx, rx, x);
24
	}
25
}
26
27
int find(int x) {
28
	return fa[x] == x ? x : find(fa[x]);
29
}
30
31
void merge(int u, int v) {
32
	u = find(u), v = find(v);
33
	if (sz[u] > sz[v]) {
34
		swap(u, v);
35
	}
36
	++top, st_u[top] = u, st_v[top] = v, st_o[top] = fa[u], st_s[top] = sz[u];
37
	if (u != v) {
38
		fa[u] = v, sz[v] += sz[u], sz[u] = 0;
39
	}
40
}
41
42
void split() {
43
	int u = st_u[top], v = st_v[top], o = st_o[top], s = st_s[top];
44
	if (u != v) {
45
		fa[u] = o, sz[v] -= s, sz[u] = s;
46
	}
47
	top--;
48
}
49
50
void insert(int x) {
51
	for (int i = 0; i < S[x].size(); i++) {
52
		merge(u[S[x][i]], v[S[x][i]]);
53
	}
54
}
55
56
void erase(int x) {
57
	for (int i = S[x].size() - 1; i >= 0; i--) {
58
		split();
59
	}
60
}
61
62
void solve(int rt, int l, int r) {
63
	insert(rt);
64
	if (l == r) {
65
		if (o[l] == 2) {
66
			ans[l] = find(u[l]) == find(v[l]);
67
		}
68
		erase(rt);
69
		return;
70
	}
71
	solve(ls, l, mid);
72
	solve(rs, mid + 1, r);
73
	erase(rt);
74
}
75
76
int main() {
77
	scanf("%d %d", &n, &m);
78
	for (int i = 1; i <= m; i++) {
79
		scanf("%d %d %d", &o[i], &u[i], &v[i]);
80
		if (u[i] > v[i]) {
81
			swap(u[i], v[i]);
82
		}
83
		if (!o[i]) {
84
			mp[make_pair(u[i], v[i])] = i;
85
		} else if (o[i] == 1) {
86
			modify(1, 1, m, mp[make_pair(u[i], v[i])], i - 1, i);
87
			mp[make_pair(u[i], v[i])] = 0;
88
		}
89
	}
90
	for (map<pair<int, int>, int>::iterator it = mp.begin(); it != mp.end(); it++) {
91
		if (it -> second) {
92
			modify(1, 1, m, it -> second, m, it -> second);
93
		}
94
	}
95
	for (int i = 1; i <= n; i++) {
96
		fa[i] = i, sz[i] = 1;
97
	}
98
	solve(1, 1, m);
99
	for (int i = 1; i <= m; i++) {
100
		if (o[i] == 2) {
101
			puts(ans[i] ? "Y" : "N");
102
		}
103
	}
104
	return 0;
105
}