1390:食物链【NOI2001】
时间限制: 1000 ms 内存限制: 65536 KB
提交数: 1806 通过数: 906
【题目描述】
动物王国中有三类动物A,B,C,这三类动物的食物链构成了有趣的环形。A吃B, B吃C,C吃A。
现有N个动物,以1-N编号。每个动物都是A,B,C中的一种,但是我们并不知道它到底是哪一种。
有人用两种说法对这N个动物所构成的食物链关系进行描述:
第一种说法是"1 X Y",表示X和Y是同类。
第二种说法是"2 X Y",表示X吃Y。
此人对N个动物,用上述两种说法,一句接一句地说出K句话,这K句话有的是真的,有的是假的。当一句话满足下列三条之一时,这句话就是假话,否则就是真话。
1)当前的话与前面的某些真的话冲突,就是假话;
2)当前的话中X或Y比N大,就是假话;
3)当前的话表示X吃X,就是假话。
你的任务是根据给定的N(1≤ N ≤50,000)和K句话(0≤K≤100,000),输出假话的总数。
【输入】
第一行是两个整数N和K,以一个空格分隔。
以下K行每行是三个正整数 D,X,Y,两数之间用一个空格隔开,其中D表示说法的种类。
若D=1,则表示X和Y是同类。
若D=2,则表示X吃Y。
【输出】
只有一个整数,表示假话的数目。
【输入样例】
100 7
1 101 1
2 1 2
2 2 3
2 3 3
1 1 3
2 3 1
1 5 5
【输出样例】
3
【提示】
【样例解释】
100122212171011231351233315假话真话真话假话假话真话真话
思路:这里我们维护每个结点到其父亲的距离来表示与父亲的关系,0代表是同类,1是吃父亲,2是被父亲吃。
因为吃与被吃的关系构成了环,所以将距离累加再对3取模就可以得到结点与其他结点的关系了,若给出的X和Y不在同一并查集内,需要合并,让一方的最终祖先指向另一方的最终祖先,就需要根据给定的关系以及两结点到其祖先的距离求出两个祖先间该有的距离;若给定的X和Y已经在同一并查集内,根据他们到祖先的距离求出他们的距离判断即可。
0 吃1 1吃 2 2 吃0
(0-1)+3 =2 (1-2)+3 =2,2-0 = 2;
c = (c - dist[a] + dist[b] + 3) % 3; 表示 x吃y
#include <cstdio>
#include<cstring>
using namespace std;
inline int get_num() {
int num = 0;
char c = getchar();
while (c < '0' || c > '9') c = getchar();
while (c >= '0' && c <= '9')
num = num * 10 + c - '0', c = getchar();
return num;
}
const int maxn = 5e4 + 5;
int fa[maxn], dist[maxn];
int find(int i) {
if (i == fa[i]) return i;
int g = fa[i];
fa[i] = find(g);
dist[i] = (dist[i] + dist[g]) % 3;
return fa[i];
}
inline void un(int a, int b, int c) {
c = (c - dist[a] + dist[b] + 3) % 3;
a = find(a), b = find(b);
fa[a] = b, dist[a] = c;
}
int main() {
int n, k, ans = 0;
n = get_num(), k = get_num();
for (int i = 1; i <= n; ++i)
fa[i] = i, dist[i] = 0; //初始化
//与父亲的距离为0是同类,1是吃父亲,2是被吃
int c, x, y;
for (int i = 1; i <= k; ++i) {
c = get_num(), x = get_num(), y = get_num();
if (x > n || y > n) ++ans;
else if (c == 2 && x == y) ++ans;
else {
if (find(x) != find(y)) un(x, y, c - 1);
else if ((dist[x] - dist[y] + 3) % 3 != c - 1) ++ans;
}
}
printf("%d", ans);
return 0;
}