Poj1182 食物链(并查集/带权并查集)
题面
题解
这里采用并查集的补集。
$x$表示同类集合,$x+n$表示敌人集合,$x+n\times2$表示敌人的敌人集合。
如果当前给出的是一对同类关系,就判断$x$是否吃$y$或者$y$是否吃$x$(冲突)。
如果是真话,就将所有关系连在一起。
反之,如果给出的是一对$x$吃$y$关系,就判断$x$是否吃了自己(矛盾)或者他们两个互相吃(冲突)。
如果是真话,将$x$的敌人和$y$的敌人的敌人(朋友)连在一起,将$y$的敌人和$x$的敌人的敌人(朋友)连在一起,即$y$的敌人是$x$的朋友,将$y$丢进$x$的敌人集合中。
上面这对话可能有点绕口,但是大概还是讲出来了,仔细理解一下吧...
这里还有一个带权并查集的做法,但是并查集补集更好写(写起来更顺些)。
#include <cstdio>
#include <cstring>
#define Rg register
const int N = 5e4 + 10;
int n, k, fa[N * 3], cz, x, y, ans, nn;
int find (int x) {
if (fa[x] != x) fa[x] = find (fa[x]);
return fa[x];
}
int unionn (int u, int v) {
int fu = find (u), fv = find (v);
fa[fv] = fu;
}//v -> u
int main () {
scanf ("%d%d", &n, &k);
nn = n * 3;
for (Rg int i = 1; i <= nn; ++i)
fa[i] = i;
while (k--) {
scanf ("%d%d%d", &cz, &x, &y);
if (x > n || y > n) {++ans; continue;}//依据2
if (cz == 1) {
if (find (x + n) == find(y) || find (y + n) == find (x)) {++ans; continue;}//依据1
unionn (x, y); unionn (x + n, y + n); unionn (x + (n << 1), y + (n << 1));
} else {
if (find (x) == find (y) || find (x) == find (y + n)) {++ans; continue;}//依据1&依据3
unionn (y + (n << 1), x); unionn (y, x + n); unionn (y + n, x + (n << 1));
}
}
printf ("%d\n", ans);
return 0;
}