zoukankan      html  css  js  c++  java
  • [luoguP2024] 食物链(并查集)

    传送门

    经典的并查集问题

    对于这种问题,并查集需要分类

    开3*n的并查集,其中x用来连接与x同类的,x+n用来连接x吃的,x+2*n用来连接x被吃的。

    1 x y时,如果 x吃y 或 x被y吃,那么为假话,

    否则x与y同类,x吃的y也吃,x被吃的y也被吃;

    2 x y时,如果 x与y同类(x与x自然也是同类) 或 y吃x,那么为假话,

    否则x吃y,y被x吃,y吃x被吃的.

    ——代码

     1 #include <cstdio>
     2 #include <iostream>
     3 #define N 1000001
     4 
     5 int n, k, ans;
     6 int f[N];
     7 
     8 inline int read()
     9 {
    10     int x = 0, f = 1;
    11     char ch = getchar();
    12     for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = -1;
    13     for(; isdigit(ch); ch = getchar()) x = (x << 1) + (x << 3) + ch - '0';
    14     return x * f;
    15 }
    16 
    17 inline int find(int x)
    18 {
    19     return x == f[x] ? x : f[x] = find(f[x]);
    20 }
    21 
    22 inline void connect(int x, int y)
    23 {
    24     x = find(x);
    25     y = find(y);
    26     if(x ^ y) f[x] = y;
    27 }
    28 
    29 int main()
    30 {
    31     int i, j, x, y, z;
    32     n = read();
    33     k = read();
    34     for(i = 1; i <= 3 * n; i++) f[i] = i;
    35     for(i = 1; i <= k; i++)
    36     {
    37         z = read();
    38         x = read();
    39         y = read();
    40         if(x > n || y > n)
    41         {
    42             ans++;
    43             continue;
    44         }
    45         if(z == 1)
    46         {
    47             if(find(x + n) == find(y) || find(x + 2 * n) == find(y))
    48             {
    49                 ans++;
    50                 continue;
    51             }
    52             connect(x, y);
    53             connect(x + n, y + n);
    54             connect(x + 2 * n, y + 2 * n);
    55         }
    56         else
    57         {
    58             if(find(x) == find(y) || find(x + 2 * n) == find(y))
    59             {
    60                 ans++;
    61                 continue;
    62             }
    63             connect(x + n, y);
    64             connect(y + 2 * n, x);
    65             connect(y + n, x + 2 * n);
    66         }
    67     }
    68     printf("%d
    ", ans);
    69     return 0;
    70 }
    View Code

    还有带权并查集的做法

    这道题的特殊之处在于对于任意一个并查集,只要告诉你某个节点的物种,你就可以知道所有节点对应的物种。

    比如一条长为4的链 甲->乙->丙->丁 ,我们知道乙是A物种。那么甲一定是B物种,因为A只吃B物种,不吃C物种或是自己的同类。同样的丙一定是C物种,丁是B物种。

    也就是说,每一条链上都是A、B、C物种循环,这也是我们寻找不合逻辑的假话的出发点。

    我们可以定义数组d[i]表示节点i到根节点距离mod3的结果帮助解题。

    我们统计谎话的数量,那么我们把谎话这样分类:

    第一类叫弱智的谎话,包括

    (1)自己吃自己的同类是谎话,表述为d==2&&x==y(其中x y是我们读入的量);

    (2)编号超出限制,表述为x>n||y>n。 第二类叫不弱智的谎话,包括d==1和d==2这样两类。

    (1)d==1。

    我们先要考虑x y是否在同一个并查集中,这是判断真假话的前提。

    如果x y 不在同一个并查集中,那么关于他们的任何表述都可以是真的。

    比如两条链:1->2->3->4->5 6->7->8->9

    如果我说1和6是同类,那么自然地,2与7,3与8,4与9成为同类。

    我们任意的选取两个数是同类都是符合的。

    下面我们要做的就是把两个并查集合并。

    d[f[x]]=(d[y]-d[x]+3)%3;//关于距离

    f[f[x]]=f[y];//关于父亲

    如果x y在同一个并查集中,那么违反距离关系的话一定是假话。

    d[x]!=d[y]//关于距离

    (2)d==2。

    还是先看x y是否在一个并查集中,如果不在,那么合并并查集;如果在,那么根据距离关系找出假话。

    ——代码

     1 #include <cstdio>
     2 #include <iostream>
     3 #define N 1000001
     4 
     5 int n, k, ans;
     6 int f[N], d[N];
     7 
     8 inline int read()
     9 {
    10     int x = 0, f = 1;
    11     char ch = getchar();
    12     for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = -1;
    13     for(; isdigit(ch); ch = getchar()) x = (x << 1) + (x << 3) + ch - '0';
    14     return x * f;
    15 }
    16 
    17 inline int find(int x)
    18 {
    19     if(x ^ f[x])
    20     {
    21         int fx = f[x];
    22         f[x] = find(f[x]);
    23         d[x] = (d[x] + d[fx]) % 3;
    24     }
    25     return f[x];
    26 }
    27 
    28 int main()
    29 {
    30     int i, j, x, y, z;
    31     n = read();
    32     k = read();
    33     for(i = 1; i <= n; i++) f[i] = i;
    34     for(i = 1; i <= k; i++)
    35     {
    36         z = read();
    37         x = read();
    38         y = read();
    39         if(x > n || y > n)
    40         {
    41             ans++;
    42             continue;
    43         }
    44         if(z == 1)
    45         {
    46             if(find(x) == find(y))
    47             {
    48                 if(d[x] ^ d[y]) ans++;
    49             }
    50             else
    51             {
    52                 d[f[x]] = (d[y] - d[x] + 3) % 3;
    53                 f[f[x]] = f[y];
    54             }
    55         }
    56         else
    57         {
    58             if(find(x) == find(y))
    59             {
    60                 if(d[x] ^ ((d[y] + 1) % 3)) ans++;
    61             }
    62             else
    63             {
    64                 d[f[x]] = (d[y] - d[x] + 4) % 3;
    65                 f[f[x]] = f[y];
    66             }
    67         }
    68     }
    69     printf("%d
    ", ans);
    70     return 0;
    71 }
    View Code


    最后是关于合并两个根时两根之间的距离的解释:

    设合并后两根距离为a(即要求的量)

    R[i]表示点i到他们原来祖先的距离,途中所有线段长都可以表示。

    注意每条边的长度是不一样的,∴R[x]+a-R[y]≠R[x],而等于x、y的距离即食物关系(大家可以往下翻,下面有关于这个的讲解)

    设该距离为t

    方程:R[x]+a-R[y]=t,整理得a=t-R[x]+R[y],当然把x与y换一下也是成立的,这取决于你的程序。

  • 相关阅读:
    使用wchar_t输入,显示中文
    MFC使用rich edit若干问题
    一种对话框嵌入MFC 文档结构效果的实现方法(一),让你的自定义对话框区域同客户区大小一起改变
    一种在MFC程序上显示jpeg图片的方法(二)曙光乍现
    一种在MFC程序上显示jpeg图片的方法(一)宁滥勿缺
    MFC---GDI之DC类杂记,以画尺子为例
    又让厂公着半天急----一例自定义MFC程序编译时LNK2019错误
    egret 引擎分析之三————egret make --egretversion xxxx 到底做了什么?
    egret 引擎分析之二————从selector.js说起
    egret 引擎分析之一————egret 命令的时候发生了什么
  • 原文地址:https://www.cnblogs.com/zhenghaotian/p/7010791.html
Copyright © 2011-2022 走看看