Poj1182 食物链(并查集/带权并查集)

题面

Poj

题解

这里采用并查集的补集。

$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;
}
posted @ 2019-01-22 14:00  water_mi  阅读(168)  评论(0编辑  收藏  举报