原来不理解这题怎么做,今天看了一下题解终于理解了。
本文转载自:POJ 1182:食物链[详细!]
题意:
A,B,C三种动物,A吃B, B吃C,C吃A。有n个动物,他们编号为1~n。
输入:第一行n,k,分别表示动物个数,给出k句话(有真有假)。接下来n行每行一句话,每句的格式为三个整数:d,x,y。x,y为动物编号,d为1时表示x,y是同类,d为2时表示x吃y。
说明:假话有三种: 1) 当前的话与前面的某些真的话冲突,就是假话; 2) 当前的话中X或Y比N大,就是假话; 3) 当前的话表示X吃X,就是假话。
输出:假话的个数。
解析:
一、 relation 的确定
对每一个元素,把它对父节点的关系用数组r[i]表示,即relation,作为权值。
由于数字d指定了后面给出的两个动物的关系:
1)
d=1
时,
d−1=0
,x,y是同类;
2)
d=2
时,
d−1=1
,x吃y
这个权值不是随便定的噢,且看下面推导~!
因此我们设r[i]包含这三种值,表示动物之间的三种关系:
1)
r[i]=0
时,
i
与
2)
r[i]=1
时,
i
被
3)
r[i]=2
时,
i
吃
注: p[i]为i的父节点。
二、路径压缩 find_set(x) 的节点算法
我们知道,并查集的集合代表元是一个元素,这里规定集合出现的第一个元素是代表元。
为了使查找效率提高,我们需要使树尽可能低,并查集的路径压缩让它只有一层。
那么怎么把将不是直接联系的两个节点成为父子关系?怎样表明他们之间的关系呢?
这是我们接下来要推导的东西
根据儿子对父亲的关系
r
,和父亲对爷爷的关系
i j k
爷爷 父亲 儿子 儿子与爷爷
0 0 0=(i + j)%3
0 1 1=(i + j)%3
0 2 2=(i + j)%3
1 0 1=(i + j)%3
1 1 2=(i + j)%3
1 2 0=(i + j)%3
2 0 2=(i + j)%3
2 1 0=(i + j)%3
2 2 1=(i + j)%3
观察即可知道,儿子对爷爷的关系是k=(i+j)%3。
为啥要这样做?
可以画个图理解下。如:X->Y,Y->Z,要把X的父节点设为Z就需要上述步骤。
需要注意的是,每次更新节点的
三、集合间关系的确定
初始时候每个元素都是自洽的,那么有几个集合的时候,怎么确定集合(代表节点)之间的关系?
因为只有确定两个集合(的代表节点)之间的关系才能把两个集合合并最后得到一个并查集。
先给出公式: r[ry]=(3−r[y]+(d−1)+r[x])
我们分3个小部分来推导:
(例如,由
Y−>X−>P,Y−>Q
,得到
Q−>P
。我们需要先得到
Q−>X
,再得到
Q−>P
)画图更易理解。
(1) 由子对父的关系得到父对子的关系
子对父
0(父子同类)
(3−0)%3=0
1(父吃子)
(3−1)%3=2
//父吃子
2(子吃父)
(3−2)%3=1
//子吃父,一样的哦亲
即
r[爷爷]=(r[子]+r[父])
(2)
Q−>X
,把Y接到X上,使Q成为Y的儿子,进而得到Q对X的
relation
权值:
r[Q对X]=(r[Y对X]+r[Q对Y])%3
r[Y对X]=d−1
r[Q对Y]=3−r[Y]
故
r[Q对X]=((d−1)+(3−r[y]))%3
(3)
Q−>P
,最后由
Q−>X−>P
得到
Q−>P
(得到Q对P的
relation
权值:
r[Q对X]=(r[Y对X]+r[Q对Y])
r[Q对P]=(r[Y对X]+r[Q对Y]+r[X对P])
故
r[Q对P]=((d−1)+(3−r[y])+r[x])
注意:
题目只有一组样例,如果按照多组来输入会WA。
my code
#include <cstdio>
#include <algorithm>
using namespace std;
const int N = (int)5e4 + 10;
int pa[N], r[N];
int ans;
int n, K;
void init() {
for(int i = 0; i <= n; i++) {
pa[i] = i;
r[i] = 0;
}
}
int find(int u) {
if(u == pa[u]) return u;
int pre = pa[u];
pa[u] = find(pre);
r[u] = (r[u] + r[pre]) % 3;
return pa[u];
}
void Union(int x, int y, int d) {
int root1 = find(x);
int root2 = find(y);
if(root1 != root2) {
pa[root2] = root1;
r[root2] = (3 + (d - 1) + r[x] - r[y]) % 3;
}else {
if(d == 1 && r[x] != r[y])
ans++;
else if(d == 2 && (3 - r[x] + r[y]) % 3 != 1)
ans++;
}
}
int main() {
int x, y, d;
scanf("%d%d", &n, &K);
init();
ans = 0;
while(K--) {
scanf("%d%d%d", &d, &x, &y);
if(x > n || y > n) {
ans++;
}else if(d == 2 && x == y) {
ans++;
}else {
Union(x, y, d);
}
}
printf("%d\n", ans);
return 0;
}