洛谷 P2024 [NOI2001] 食物链

1 洛谷P2024 [NOI2001] 食物链

2 题目描述

时间限制 \(1s\) | 空间限制 \(125M\)

动物王国中有三类动物 \(A,B,C\), 这三类动物的食物链构成了有趣的环形。\(A\)\(B, B\)\(C, C\)\(A\)

现有 \(N\) 个动物,从 \(1\)\(N\) 编号。每个动物都是 \(A, B, C\) 中的一种,但是我们并不知道它到底是哪一种。

有人用两种说法对这 \(N\) 个动物所构成的食物链关系进行描述:

  • 第一种说法是 \(1 \space X \space Y\),表示 \(X\)\(Y\) 是同类。
  • 第二种说法是 \(2 \space X \space Y\),表示 \(X\)\(Y\)

此人对 \(N\) 个动物,用上述两种说法,一句接一句地说出 \(K\) 句话,这 \(K\) 句话有的是真的,有的是假的。当一句话满足下列三条之一时,这句话就是假话,否则就是真话。

  • 当前的话与前面的某些真的话冲突,就是假话
  • 当前的话中 \(X\)\(Y\)\(N\) 大,就是假话
  • 当前的话表示 \(X\)\(X\),就是假话

你的任务是根据给定的 \(N\)\(K\) 句话,输出假话的总数。

数据范围:
\(1 ≤ N ≤ 5 \times 10^4\); \(1 ≤ K ≤ 10^5\)

3 题解

假话的判定中第二条和第三条十分简单,只需要简单模拟即可。

我们主要关注与之前的真话冲突这一条判定。我们考虑一句话如何才能不与之前的话冲突:

如果这句话说的是 \(X\)\(Y\),那么 \(X\) 一定不和 \(Y\) 为同类,\(Y\) 一定不会吃 \(X\),并且不存在一个 \(Z\) 使得 \(Z\)\(Y\)\(X\)\(Z\)(原因请读者自行思考)。

如果这句话说的是 \(X\)\(Y\) 为同类,那么 \(X\) 一定不会吃 \(Y\)\(Y\) 也一定不会吃 \(X\)

我们这时发现,\(X\)\(Y\) 是否为同类和谁吃谁的信息有点像连通性的问题,于是我们可以考虑使用并查集维护。

这个时候问题来了:是否是同类可以轻松用并查集维护,但是谁吃了谁就不好维护了。就算再开一个并查集把吃别的生物与被吃的生物都联通起来,也没法知道到底是谁吃谁。

这个时候,我们换一个思路,不从并查集本身进行改变,而是去改变元素本身。这个想法就叫扩展域并查集。

具体地来说,我们将每一个生物都拆分成三个元素:\(X_A\)(自己的物种),\(X_B\)(被自己吃的物种),\(X_C\)(吃自己的物种)。

这个时候,我们将 \(X_A\)\(Y_B\) 连接到同一集合中,就代表了 \(X\)\(Y\)

因此,我们判断 \(X\)\(Y\) 是否是假话的依据就变成了:

\(X_A \ne Y_A, X_C \ne Y_A, Y_C \ne X_B\)

判断 \(X\)\(Y\) 是同类是否是假话的依据就变成了:

\(X_B \ne Y_A, X_C \ne Y_A\)

(这里的不等号指的是不在同一集合内,下文中的等号指的是在同一集合内)

这里注意,如果 \(X\)\(Y\) 成立,那么令 \(X_B = Y_A, Y_C = X_A, Y_B = X_C\)。如果 \(X\)\(Y\) 是同类成立,那么令 \(X_A = Y_A, X_B = Y_B, X_C = Y_C\)

我们可以用 \(X\) 表示 \(X_A\),用 \(X + N\) 表示 \(X_B\),用 \(X + 2\times N\) 表示 \(X_C\)

4 代码(空格警告):

#include <iostream>
#include <cstdio>
using namespace std;
const int N = 5e4+10, M = 1e5+10;
int n, k, opt, x, y, cnt, xa, xb, xc, ya, yb, yc;
int fa[N * 3];
int find(int x)
{
    if (fa[x] == x) return x;
    return fa[x] = find(fa[x]);
}
int read()
{
    int x = 0, f = 1;
    char c = getchar();
    while (c < '0' || c > '9')
    {
        if (c == '-') f = -1;
        c = getchar();
    }
    while (c >= '0' && c <= '9')
    {
        x = (x << 1) + (x << 3) + (c ^ 48);
        c = getchar();
    }
    return x * f;
}
int main()
{
    n = read();
    k = read();
    for (int i = 1; i <= n * 3; i++) fa[i] = i;
    for (int i = 1; i <= k; i++)
    {
        opt = read(), x = read(), y = read();
        if (x > n || y > n)
        {
            cnt++;
            continue;
        }
        xa = find(x);
        xb = find(x + n);
        xc = find(x + n * 2);
        ya = find(y);
        yb = find(y + n);
        yc = find(y + n * 2);
        if (opt == 1)
        {
            if (xb == ya || xc == ya)
            {
                cnt++;
                continue;
            }
            fa[xa] = ya;
            fa[xb] = yb;
            fa[xc] = yc;
        }
        else
        {
            if (xa == ya || xc == ya || yc == xb)
            {
                cnt++;
                continue;
            }
            fa[xb] = ya;
            fa[yc] = xa;
            fa[yb] = xc;
        }
    }
    printf("%d", cnt);
    return 0;
}

欢迎关注我的公众号:智子笔记

posted @ 2021-07-09 21:41  David24  阅读(46)  评论(0编辑  收藏  举报