0x41 并查集
P1955 [NOI2015] 程序自动分析
怎么写过。算了再写一遍。
考虑到 $i, j \le 10^9$,我们还要离散化一下,这么变态。
整体还是非常显然的,注意到相等关系具有传递性,用并查集维护即可。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
| using i3 = array<int, 3>;
void solve() {
int n;
cin >> n;
vector<i3> vec(n);
vector<int> d;
for (auto &[i, j, e] : vec) {
cin >> i >> j >> e;
d.emplace_back(i);
d.emplace_back(j);
}
sort(d.begin(), d.end());
d.erase(unique(d.begin(), d.end()), d.end());
DSU dsu(d.size() + 1);
for (auto &[i, j, e] : vec) {
i = lower_bound(d.begin(), d.end(), i) - d.begin();
j = lower_bound(d.begin(), d.end(), j) - d.begin();
if (e == 1) {
dsu.Merge(i, j);
}
}
for (auto &[i, j, e] : vec) {
if (e)
continue;
if (dsu.Find(i) == dsu.Find(j)) {
cout << "NO\n";
return;
}
}
cout << "YES\n";
}
|
UVa 不写,略。
P1196 [NOI2002] 银河英雄传说
我擦,废话怎么那么多。
考虑我们需要维护一个节点和其父节点的距离,那么显然这个是 $\tt{louisdlee}$ 最喜欢的带权并查集。
然后我们只要简单魔改一下这个并查集的模板就可以了。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
| struct DSU {
std::vector<int> f, sz;
std::vector<int> d;
DSU(int n) : d(n), f(n), sz(n, 1) { std::iota(f.begin(), f.end(), 0); }
int Find(int x) {
if (f[x] == x)
return x;
int fa = Find(f[x]);
d[x] += d[f[x]];
return f[x] = fa;
}
void Merge(int x, int y) {
x = Find(x), y = Find(y);
if (x == y)
return;
f[y] = x, d[y] = sz[x];
sz[x] += sz[y];
}
};
using i3 = array<int, 3>;
void solve() {
int m;
cin >> m;
vector<i3> vec(m);
set<int> st;
for (auto &[op, i, j] : vec) {
char ch;
cin >> ch >> i >> j;
st.insert(i); st.insert(j);
op = ch == 'C';
}
DSU dsu(st.size() + 1);
for (auto &[op, i, j] : vec) {
if (op == 0) {
dsu.Merge(j, i);
} else {
if (dsu.Find(i) != dsu.Find(j)) {
cout << "-1\n";
} else {
cout << abs(dsu.d[i] - dsu.d[j]) - 1 << "\n";
}
}
}
}
|
P5937 [CEOI1999] Parity Game
感觉很变态啊。
考虑奇偶不难想到异或,然后我们思考一下前缀异或和 $\rm{sum}_{l-1} \oplus \rm{sum}_r$ 异或出来就是区间 $[l,r]$ 的奇偶性。然后我们用并查集维护每一个区间端点 $\rm{sum}_i$ 不难发现对于给定的 $l, r$ 和 $\rm{ans}$,在异或边权的意义上有传递性,然后直接用带权并查集维护即可。
考虑 $n \le 10^9$ 还要离散化一下。
我算是知道了为什么那么多人不封装的并查集了。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
| struct DSU {
std::vector<int> f, sz;
std::vector<int> d;
DSU(int n) : d(n), f(n), sz(n, 1) { std::iota(f.begin(), f.end(), 0); }
int Find(int x) {
if (f[x] == x)
return x;
int fa = Find(f[x]);
d[x] ^= d[f[x]];
return f[x] = fa;
}
};
using i3 = array<int, 3>;
void solve() {
int n, m;
cin >> n >> m;
vector<i3> ask(m);
vector<int> d;
for (auto &[l, r, ans] : ask) {
string op;
cin >> l >> r >> op;
l--;
ans = op.size() % 2;
d.emplace_back(l), d.emplace_back(r);
}
sort(d.begin(), d.end());
d.erase(unique(d.begin(), d.end()), d.end());
DSU dsu(d.size() + 1);
for (int i = 0; auto &[l, r, ans] : ask) {
l = lower_bound(d.begin(), d.end(), l) - d.begin();
r = lower_bound(d.begin(), d.end(), r) - d.begin();
int p = dsu.Find(l), q = dsu.Find(r);
if (p == q) {
if ((dsu.d[l] ^ dsu.d[r]) != ans) {
cout << i << "\n";
return;
}
} else {
dsu.f[p] = q, dsu.d[p] = dsu.d[l] ^ dsu.d[r] ^ ans;
}
i++;
}
cout << m << "\n";
}
|
P2024 [NOI2001] 食物链
考虑开一个大小为 $3n$ 的并查集,其中 $[1,n]$ 维护同类信息, $[n+1, 2n]$ 维护捕食信息,然后 $[2n+1,3n]$ 维护天敌信息。
若 $X$ 吃 $Y$,则合并 $(X+n, Y)$,$(X,Y+2n)$ 和 $(X+2n, Y+n)$,若 $X$ 与 $Y$ 为同类,则合并 $(X, Y)$,$(X+n, Y+n)$ 和 $(X+2n, Y+2n)$。处理每一句话之前判断每句话的真假(即对于 $X$ 与 $Y$ 同类,$X+n \nsim Y$ 且 $X \nsim Y$;对于 $X$ 吃 $Y$,$X \nsim Y$ 且 $X \nsim Y+n$)即可。
需要注意还需要判断 $X>n \lor Y>n$ 的情况。
![CODE]-
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
| void solve() {
int n, k;
cin >> n >> k;
DSU dsu(4 * n);
int ans = 0;
while (k--) {
int op, x, y;
cin >> op >> x >> y;
if (x > n or y > n) {
ans++;
continue;
}
if (op == 1) {
if (dsu.Same(x + n, y) or dsu.Same(x, y + n)) {
ans++;
continue;
}
dsu.Merge(x, y);
dsu.Merge(x + n, y + n);
dsu.Merge(x + 2 * n, y + 2 * n);
} else {
if (dsu.Same(x, y) or dsu.Same(x, y + n)) {
ans++;
continue;
}
dsu.Merge(x + n, y);
dsu.Merge(x, y + 2 * n);
dsu.Merge(x + 2 * n, y + n);
}
}
cout << ans <<"\n";
}
|
先写这么多,吃饭了。