• 240.食物链(并查集的扩展,维护额外信息)


    240. 食物链

    动物王国中有三类动物 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大,就是假话;

    • 当前的话表示 X 吃 X 就是假话。
    • 你的任务是根据给定的 N和 K句话,输出假话的总数。

      输入格式

      第一行是两个整数 N

      和 K

      ,以一个空格分隔。

      以下 K

      行每行是三个正整数 D,X,Y,两数之间用一个空格隔开,其中 D

      表示说法的种类。

      若 D=1

      ,则表示 X 和 Y

      是同类。

      若 D=2

      ,则表示 X 吃 Y

      输出格式

      只有一个整数,表示假话的数目。

      数据范围

      1≤N≤50000

      ,
      0≤K≤100000

      输入样例:

      1. 100 7
      2. 1 101 1
      3. 2 1 2
      4. 2 2 3
      5. 2 3 3
      6. 1 1 3
      7. 2 3 1
      8. 1 5 5

      输出样例:

      3
      

     * 题意分析,不管两个动物是属于同类还是敌对关系,都放在同一个集合中,
     * 当两个动物编号不同时,只要他们属于不同的集合中,不管他们是以同类
     * 的信息出现还是以敌对的信息出现,这句话都是对的,并且把它加入集合
     * 当中;
     * 假设 A吃B,B吃C,C吃A,那么将A放在第0层,B放在第一层,C放在第二层;
     * 那么假设x吃y 就能得到 (d[x]-d[y]+1)%3 == 0;
     * 如果x与y是同类,那么能得到 (d[x]-d[y])%3 == 0;
     * 其他的具体操作在代码中体现;

    * 为什么if语句里面需要返回 t 的值,这是因为当fath[x] 不是根节点的时
     * 候,find(fath[x]) 并没有返回值,也就是说t中的值是未知的,这就无法
     * 正确更新下面d数组以及fath数组中的值;
     * 也就是说这样写是错的:
     *  int find(int x)
        {
            if (fath[x] != x)
            {
                int t = find(fath[x]);
                d[x] += d[fath[x]];
                fath[x] = t;
            }
            else
                return x;
        }
        
        这样才是OK的:
        int find(int x)
        {
            if (fath[x] != x)
            {
                int t = find(fath[x]);
                d[x] += d[fath[x]];
                fath[x] = t;
                return t;
            }
            else
                return x;
        }
        
        亦或者不加else,直接返回fath[x](请注意这儿是返回fath[x],而不是返回
        x),就不用返回t了;
        int find(int x)
        {
            if (fath[x] != x)
            {
                int t = find(fath[x]);
                d[x] += d[fath[x]];
                fath[x] = t;
            }
            return fath[x];
        }
     *
     * 但是你也会觉得以往的并查集中if语句没有return 语句也并没有错,这
     * 是怎么回事呢?
     * eg:
     *  int find(int u)
        {
            if(fath[u] >= 0)
                 fath[u] = find(fath[u]);
            else
                return u;
        }
        
        这个并查集是因为if语句中只有一个语句,不需要去维护什么额外信息,
        它只是单纯的返回根节点的下标;

     coding :

    1. /**
    2. * 题意分析,不管两个动物是属于同类还是敌对关系,都放在同一个集合中,
    3. * 当两个动物编号不同时,只要他们属于不同的集合中,不管他们是以同类
    4. * 的信息出现还是以敌对的信息出现,这句话都是对的,并且把它加入集合
    5. * 当中;
    6. * 假设 A吃B,B吃C,C吃A,那么将A放在第0层,B放在第一层,C放在第二层;
    7. * 那么假设x吃y 就能得到 (d[x]-d[y]+1)%3 == 0;
    8. * 如果x与y是同类,那么能得到 (d[x]-d[y])%3 == 0;
    9. * 其他的具体操作在代码中体现;
    10. */
    11. #include <iostream>
    12. #include <algorithm>
    13. using namespace std;
    14. const int maxn = 1e5+10;
    15. int fath[maxn]; //存放每个编号的祖宗结点编号
    16. int d[maxn]; //与祖宗结点的距离
    17. /**
    18. * 为什么if语句里面需要返回 t 的值,这是因为当fath[x] 不是根节点的时
    19. * 候,find(fath[x]) 并没有返回值,也就是说t中的值是未知的,这就无法
    20. * 正确更新下面d数组以及fath数组中的值;
    21. * 也就是说这样写是错的:
    22. * int find(int x)
    23. {
    24. if (fath[x] != x)
    25. {
    26. int t = find(fath[x]);
    27. d[x] += d[fath[x]];
    28. fath[x] = t;
    29. }
    30. else
    31. return x;
    32. }
    33. 这样才是OK的:
    34. int find(int x)
    35. {
    36. if (fath[x] != x)
    37. {
    38. int t = find(fath[x]);
    39. d[x] += d[fath[x]];
    40. fath[x] = t;
    41. return t;
    42. }
    43. else
    44. return x;
    45. }
    46. 亦或者不加else,直接返回fath[x](请注意这儿是返回fath[x],而不是返回
    47. x),就不用返回t了;
    48. int find(int x)
    49. {
    50. if (fath[x] != x)
    51. {
    52. int t = find(fath[x]);
    53. d[x] += d[fath[x]];
    54. fath[x] = t;
    55. }
    56. return fath[x];
    57. }
    58. *
    59. * 但是你也会觉得以往的并查集中if语句没有return 语句也并没有错,这
    60. * 是怎么回事呢?
    61. * eg:
    62. * int find(int u)
    63. {
    64. if(fath[u] >= 0)
    65. fath[u] = find(fath[u]);
    66. else
    67. return u;
    68. }
    69. 这个并查集是因为if语句中只有一个语句,不需要去维护什么额外信息,
    70. 它只是单纯的返回根节点的下标;
    71. */
    72. int find(int x)
    73. {
    74. if (fath[x] >= 0)
    75. {
    76. int t = find(fath[x]);
    77. d[x] += d[fath[x]];
    78. fath[x] = t;
    79. return t;
    80. }
    81. else
    82. return x;
    83. }
    84. int main()
    85. {
    86. fill(fath,fath+maxn,-1); //初始所有节点都没有在任何集合中
    87. int n,m;
    88. int res=0;
    89. cin >> n >> m;
    90. for(int i=0;i<m;++i)
    91. {
    92. int t,x,y;
    93. cin >> t >> x >> y;
    94. if(x>n || y>n) ++res;
    95. else
    96. {
    97. int fax=find(x),fay=find(y); //求出x和y所属集合的编号
    98. if(t==1) //如果是同类
    99. {
    100. if(fax == fay && (d[x]-d[y])%3) ++res; //如果在同一集合中但是不是同类
    101. else
    102. {
    103. if(fax!=fay) //如果不在同一集合中
    104. {
    105. fath[fax]=fay; //将fax集合加到fay集合中
    106. d[fax]=d[y]-d[x]; //更新fax的“距离”
    107. }
    108. }
    109. }
    110. else
    111. {
    112. if(fax==fay && (d[x]-d[y]+1)%3) ++res; //如果属于同一集合但是敌对关系的信息不符合
    113. else
    114. {
    115. if(fax!=fay) //如果不属于同一集合
    116. {
    117. fath[fax]=fay; //将fax集合加到fay集合中
    118. d[fax]=d[y]-d[x]-1; //更新fax的“距离”
    119. }
    120. }
    121. }
    122. }
    123. }
    124. cout << res << endl;
    125. return 0;
    126. }

  • 相关阅读:
    Spring Boot 集成MyBatis-Plus
    值得反复研读的表连接之SORT MERGE JOIN方式
    前端Canvas入门——怎么用Canvas画一些简单的图案
    opencv 通过滑动条调整阈值处理、边缘检测、轮廓检测、模糊、色调调整和对比度增强参数 并实时预览效果
    用golang container/list 实现队列并控制并发
    0X JavaSE-- 并发编程()
    力扣(LeetCode)25. K 个一组翻转链表(C++)
    Java线上云酒馆单预约系统源码小程序源码
    Java项目:ssm在线考试系统
    meterpreter后期攻击使用方法
  • 原文地址:https://blog.csdn.net/qq_51825761/article/details/126903232