链接:https://vjudge.net/problem/POJ-1182
题意:
动物王国中有三类动物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),输出假话的总数。
思路:
带权并查集。
放一个大佬的链接吧:https://blog.csdn.net/freezhanacmore/article/details/8767413
这种题做的头疼,维护一个表示i与i的根节点关系的数组。
找根节点的时候更新,合并两个集合也要找关系。
代码:
#include <iostream> #include <memory.h> #include <string> #include <istream> #include <sstream> #include <vector> #include <stack> #include <algorithm> #include <map> #include <queue> #include <math.h> using namespace std; const int MAXN = 50000+10; int Father[MAXN],Rank[MAXN]; int n,k; void Init() { for (int i = 1;i<=n;i++) { Father[i] = i; Rank[i] = 0; } } int Get_F(int x) { if (Father[x] == x) return x; int tmp = Father[x]; Father[x] = Get_F(Father[x]); Rank[x] = (Rank[x] + Rank[tmp])%3;//当前点与根节点的关系为当前点与父节点和父节点与根节点 return Father[x]; } void Union(int op,int l,int r) { int fl = Get_F(l); int fr = Get_F(r); Father[fr] = fl; Rank[fr] = (3-Rank[r] + op - 1 + Rank[l])%3; } int main() { scanf("%d%d",&n,&k); Init(); int op,l,r; int cnt = 0; for (int i = 1;i<=k;i++) { scanf("%d%d%d",&op,&l,&r); if (l > n||r > n) cnt++; else if (l == r) { if (op == 2) cnt++; } else if (Get_F(l) == Get_F(r)) { if (op == 1 && Rank[l] != Rank[r]) cnt++; if (op == 2 && (Rank[l]+1)%3 != Rank[r]) cnt++; } else { Union(op,l,r); } //cout << cnt << endl; } printf("%d\n",cnt); return 0; }