luogu2024 食物链
題目大意
動物王國中有三類動物 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 句話有的是真的,有的是假的。當一句話滿足下列三條之一時,這句話就是假話,否則就是真話。
? 當前的話與前面的某些真的話沖突,就是假話
? 當前的話中 X 或 Y 比 N 大,就是假話
? 當前的話表示 X 吃 X,就是假話
你的任務是根據給定的 N 和 K 句話,輸出假話的總數。
題解
這道題我們用到了帶權并查集。這種并查集的權值滿足疊加性,也就是唯一存在一個函數f(a,b),使得結點cur與cur->Father->Father的關系=f(cur與cur->Father的關系,cur->Father與cur->Father->Father的關系)。這道題中,我們發現f(a吃b,b吃c)=a被吃c;f(a吃b,b被吃c)=a同類c;f(a被吃b,b同類c)=a吃c。我們令“吃”為1,“被吃”為2,“同類”為0,f(a,b)=(a+b)%3正好滿足該要求。所以我們按照順序嘗試將命題涉及的兩個動物加入并查集中,并判斷是否為假話即可。
以下我們將cur與cur->Father的關系簡稱為cur->ToFaRel。首先,如何壓縮路徑?明確情況:當FindRoot(cur->Father)后,cur->Father->Father==root。此時我們要讓cur->Father=root。由f的定義,可得新的cur->ToFaRel = f(cur->ToFaRel, cur->Father->ToFaRel)。
那么如何將兩個集合合并呢?明確情況,結點a->Father==a的Root,b->Father==b的Root。明確目的,我們要讓a->Father->Father=b->Father。由f的定義,a與b->Father的關系等于f(a與b的關系,b與b->Father的關系)=f(a與a->Father的關系,a->Father與b->Father的關系)。所以a->Father->ToFaRel = Rel(a,b)+b->ToFaRel-a->ToFaRel。判斷是否合法也同理。
#include <cstdio> #include <cstring> #include <algorithm> using namespace std;const int MAX_NODE = 50010;struct UnionFindSet { private:struct Node{Node *Father;int ToFaRel;//relationship between this and father}_nodes[MAX_NODE];int _vCount;void Join(Node *a, Node *b, int toFaRel){a->ToFaRel = toFaRel;a->Father = b;}Node *FindRoot(Node *cur){if (cur->Father == cur)return cur;Node *root = FindRoot(cur->Father);Join(cur, root, (cur->ToFaRel + cur->Father->ToFaRel) % 3);return root;}public:UnionFindSet(int n){_vCount = n;for (int i = 1; i <= _vCount; i++)_nodes[i].Father = _nodes + i;}bool Join(int aId, int bId, int rel){Node *a = _nodes + aId, *b = _nodes + bId;Node *root1 = FindRoot(a), *root2 = FindRoot(b);if (root1 == root2)return (rel + b->ToFaRel) % 3 == a->ToFaRel;else{Join(root1, root2, ((rel + b->ToFaRel - a->ToFaRel) % 3 + 3) % 3);return true;}} };int main() {int n, opCnt, ans = 0;scanf("%d%d", &n, &opCnt);static UnionFindSet g(n);while (opCnt--){int op, a, b;scanf("%d%d%d", &op, &a, &b);if (a > n || b > n){ans++;continue;}else if (a == b && op == 2){ans++;continue;}switch (op){case 1:ans += !g.Join(a, b, 0);break;case 2:ans += !g.Join(a, b, 1);break;}}printf("%d\n", ans);return 0; }
轉載于:https://www.cnblogs.com/headboy2002/p/9427507.html
總結
以上是生活随笔為你收集整理的luogu2024 食物链的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 从江宁龙眠大道,坐什么车到,南京玄武区,
- 下一篇: 从济宁汽车总站到济安桥路和长青路交叉口坐