• 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
    ", ans);
        return 0;
    }
    

      

  • 相关阅读:
    如何编写vue的javascript代码结构
    二进制文件下载兼容写法
    swtich
    报错:Uncaught ReferenceError: JSENCRYPT_VERSION is not defined
    use application gateway to expose aks service over http/https
    quicksort和第k小元素问题
    quick sort
    高精度加法
    滑动窗口的最大值
    字典树
  • 原文地址:https://www.cnblogs.com/headboy2002/p/9427507.html
Copyright © 2020-2023  润新知