[并查集]食物链
经典老题了
上次做是开一个3n的并查集。
对同类的处理比较平凡。
对X吃Y:
如果发现实际上X连接了模意义下距离为0或2的Y,说明这句话是假的。
顺时针循环连接X与下一块中的Y, 使Y与X在模意义下距离为1。(Y=X++)
#include<bits/stdc++.h>
using namespace std;
int n, k, ans, f[200005];
int find (int x) {return x == f[x] ? f[x] : f[x] = find(f[x]);}
bool check (int x, int y) {return find(x) == find(y);}
void merge (int x, int y) {f[find(x)] = find(y);}
int main() {
cin >> n >> k;
for (int i = 1; i <= n * 3; i++) f[i] = i;
for (int i = 1; i <= k; i++) {
int opt, x, y;
cin >> opt >> x >> y;
if (x > n || y > n) ans++;
else if (opt == 1) {
if (check(x, y + n) || check(x, y + 2 * n)) ans++;
else {
merge(x, y);
merge(x + n, y + n);
merge(x + 2 * n, y + 2 * n);
}
}
else if(opt == 2) {
if (check(x, y) || check(x, y + 2 * n)) ans++;
else {
merge(x, y + n);
merge(x + n, y + 2 * n);
merge(x + 2 * n, y);
}
}
}
cout << ans;
return 0;
}
这次根据acwing的思路整了个带权并查集,通过维护与根节点的距离判断种类。
#include <iostream>
#include <cstdio>
using namespace std;
const int maxn = 5e4 + 10;
int n, m;
int f[maxn], d[maxn];
int find(int x) {
//x == f[x] ? f[x] : f[x] = find(f[x]);
if (x != f[x]) {
int t = find(f[x]);//找根并更新d[f[x]]
d[x] += d[f[x]];//更新d[x]为到根的距离
f[x] = t;
}
return f[x];
}
int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++) f[i] = i;
int ans = 0;
while (m--) {
int op, x, y;
scanf("%d%d%d", &op, &x, &y);
if (x > n || y > n) ans++;
else {
int px = find(x), py = find(y);
if (op == 1) {
if (px == py && (d[x] - d[y]) % 3 != 0) ans++;
else if (px != py) {
f[px] = py;
//(dx + ? - dy) mod 3 = 0
d[px] = d[y] - d[x];
}
}
if (op == 2) {
//dx - dy 同余 1
if (px == py && (d[x] - d[y] - 1) % 3 != 0) ans++;
else if (px != py) {
f[px] = py;
//(dx + ? - dy - 1) mod 3 = 0
d[px] = d[y] + 1 - d[x];
}
}
}
}
printf("%d", ans);
return 0;
}