题目链接 http://poj.org/problem?id=1182
第一种 扩展域
对于几个编号为x的动物 我们用x表示它的同类域,x+n表示它的捕食域,x+2*n表示它的天地域。
我们用并查集来维护动物之间的关系,也即维护这三种域之间的关系
当给定一个指令 1 x y 时,说明x和y是同类,什么情况下这个话是错误的呢,当
x捕食y,或者y捕食x的时候这句话是错的,所以此时我们就查询 x与y+n y与x+n是否有共同祖先
1 如果有,说明它俩在同一个集合中,那它俩就不可能同类,这句话就是错误的
2 如果没有,那么这句话就是对的,那么我们就合并 x与y,x+n与y+n,x+2*n与y+2*n,把它们并到一个集合中。
当给定一个指令 2 x y时,说明x捕食y,什么情况下这个话是错误的呢,当x和y时同类,或者y捕食x,那么这句话就是错误的。
此时我们就查询 x与y,x与y+n是否是否有共同祖先
如果有,说明它俩在同一个集合中,这句话就是错误的
如果没有,那么这句话就是对的,那么我们就合并 x+n与y,x与y+2*n,把它们并到一个集合中。
注意食物链是一个环,x捕食y也说明了 y的捕食对象是x的天敌,因此此时我们再合并 y+n与x+2*n

#include<bits/stdc++.h> using namespace std; const int M = 5e4 + 5; int fa[3 * M]; int n, k,cnt; int get(int x) { if (fa[x] == x) return x; return fa[x] = get(fa[x]); } bool same(int x, int y) { return get(x) == get(y); } void merge(int x, int y) { fa[get(x)] = get(y); } int main(){ cin >> n >> k; for (int i = 0; i <= 3 * n; i++) { fa[i] = i; // i表示同类域 i+n表示捕食域 i+2*n表示天敌域 } while (k--) { int op, x, y; scanf("%d%d%d", &op, &x, &y); if (x > n || y > n || x < 1 || y < 1) { cnt++; continue; } if (op == 1) { if (same(x, y + n) || same(x + n, y) ) cnt++; else { merge(x, y); merge(x + n, y + n); merge(x + 2 * n, y + 2 * n); } } else { if (same(x, y) || same(x, y + n)) cnt++; else { merge(x + n, y); merge(x, y + 2*n); merge(x + 2 * n, y + n); } } } cout << cnt << endl; return 0; }
第二种 边带权
我们给编号为x的动物一个权值d[x] ,这个权值为它和父节点之间的关系
d[x]=0说明它和父节点同类,=1说明它捕食父节点,=2说明它被父节点捕食。
我们在路径压缩的过程同时更新权值d[x],x的父节点之前是fa[x],压缩后父节点变成了根节点,所以父节点变了,权值也应当发生改变
d[x]=(d[x]+d[fa[x]])%3
对于给的指令 d x y,如果x和y有共同根节点,说明他们在一个集合,那从在这个集合中,x与y已经有了共同根节点,且深度都为1,x与y的关系可以表示为d[x]+d[fa[x]->y]
d[fa[x]->y]就是 3-d[y] ,所以x与y的关系可以表示为
(d[x]+3-d[y])%3
如果(d[x]+3-d[y])%3 != d-1,(d为1,表示x和y同类,那么d-1就是0,这里不矛盾,d为2也是一样) 既x和y不满足所给关系,那这句话就是错的
如果x和y没有共同根节点,那么它俩的关系还不清楚,所以就合并它俩所在的集合,p和q分别表示x和y的根节点
我们让fa[p]=q;
此时我们更改了p的父节点,那它的权值d[p]也要发生相应改变,那这是一种怎样的传递关系呢?我们假设d[p]是我们要求的值,关系如下
(d[x]+d[p](此时p的父节点已经为q,这里d[p]就是我们要求的)+d[q->y])%3=d-1
d[q->y]=3-d[y](q是y的父节点)
所以可以得到d[p] = (d[y] - d[x] + 3 + d - 1) % 3;
这样就行啦。

#include<bits/stdc++.h> using namespace std; const int M = 5e4 + 5; int fa[M], d[M]; //0表示与父节点同类 1表示吃父节点 2表示被吃 int n, k, cnt; int get(int x) { if (fa[x] == x) return x; int root = get(fa[x]); d[x] = (d[x] + d[fa[x]]) % 3; return fa[x] = root; } void merge(int x, int y) { fa[get(x)] = get(y); } int main(){ cin >> n >> k; for (int i = 0; i <= n; i++) { fa[i] = i; } while (k--) { int op, x, y; scanf("%d%d%d", &op, &x, &y); if (x > n || y > n || x < 1 || y < 1) { cnt++; continue; } int p = get(x), q = get(y); if (p == q) { if ((d[x] + 3 - d[y]) % 3 != op - 1) cnt++; continue; } fa[p] = q; d[p] = (d[y] - d[x] + 3 + op - 1) % 3; } cout << cnt; return 0; }