题目链接

题目

题目描述

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

3) 当前的话表示X吃X,就是假话。

你的任务是根据给定的N(1≤N≤50,000)和K句话(0≤K≤100,000),输出假话的总数。

输入描述

第一行是两个整数N和K,以一个空格分隔。

以下K行每行是三个正整数 D,X,Y,两数之间用一个空格隔开,其中D表示说法的种类。

若D=1,则表示X和Y是同类。

若D=2,则表示X吃Y。

输出描述

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

示例1

输入

100 7
1 101 1
2 1 2
2 2 3
2 3 3
1 1 3
2 3 1
1 5 5

输出

3

说明

题解

方法一

知识点:并查集。

用权值代表其关于根节点的种类,根节点设为 \(0\) ,其他的同类为 \(0\) ,根节点是猎物为 \(1\) ,根节点是天敌为 \(2\) 。

因为种类形成环状具有传递性关系,因此路径压缩可以利用自身权值加父节点路径压缩后的权值对 \(3\) 取模即可,递归实现。

合并集合时,已知两个节点 \(a\) 和 \(b\) 路径压缩后的权值以及\(a\) 关于 \(b\) 的权值,要求出集合根节点 \(A\) 认 \(B\) 为父后的权值,因为具有环状传递性,所以可以利用向量的思想,\(\vec{AB} = -\vec{aA}+\vec{ab}+\vec{bB}\) ,随后对 \(|\vec{AB}|\) 模 \(3\) 即可,注意不要出现负数。

如果给出的关系的两个对象已经在同一个关系集合,那么检查他们关系是否和给出的条件吻合,即 \(\vec{ab} == \vec{aA} - \vec{bA}\) ,左边是条件右边是已有的关系,不吻合的答案加一。

时间复杂度 \(O(k\log n)\)

空间复杂度 \(O(n)\)

方法二

知识点:并查集。

实际上第一种解法较为繁琐,我们只关心条件之间是否矛盾,即给出的条件的两个对象已经建立了关系,检测已有关系和给出的关系是否矛盾。因此可以用扩展域并查集,其把元素的所有可能种类扩展各个独立元素,只对有具体种类的元素建立关系集合中的具体等价类(等价类的元素会同时出现),而不把相关的具体等价类并在一个集合产生完整的关系集合,利用权值进行相对分类(带权并查集是记录了一整个关系集合,并用权值做了相对根节点的关系划分),而这对于检测矛盾已经足够了。

具体地说,一个动物元素只有三种种类,我们记为 \(A\),\(B\),\(C\),其中 \(A\) 吃 \(B\) , \(B\) 吃 \(C\) , \(C\) 吃 \(A\) 。扩展域并查集把每个动物元素扩展成这 \(3\) 个有具体种类的动物元素,分别放在 $[1,n],[n+1,2n],[2n+1,3n] $ 中。

假设给出 \(a\) 吃 \(b\),则会合并三个等价类 \([a]\) 与 \([b+n]\) , \([a+n]\) 与 \([b+2n]\) , \([a+2n]\) 与 \([b]\) ,表示 \(a\) 是 \(A\) 时 \(b\) 一定是 \(B\), \(a\) 是 \(B\) 时 \(b\) 一定是 \(C\),\(a\) 是 \(C\) 时 \(b\) 一定是 \(A\) ,这样就根据条件合并了两个对象的三组具体种类的等价类。注意一个条件一定能合并三组等价类,因为这三个等价类是一个关系集合的三个具体种类表现,同样的一个等价类出现一定有其余两个等价类,且他们种类刚好补全所有情况。比如, \([a] = [b]\) 出现则一定有 \([a+n] = [b+n]\) 和 \([a+2n] = [b +2n]\),因为他们是一个同一个相对关系(同类)的三个具体表现。

另一方面,对于 \(a\) 吃 \(b\) 的条件,如果它们已经在一个关系集合(已有相对关系),则它们之间一定产生了三个等价类,而如果这些等价类刚好是 \([a] = [b]\) 或者 \([a] = [b+2n]\) ,即表达 \(a\) 与 \(b\) 同类或者 \(a\) 的天敌是 \(b\) 就很容易判断出已知条件与给出的这个条件矛盾。

因此,扩展域并查集能够维护元素不同种类之间同时出现的集合,即等价类,容易直接判断出条件是否矛盾。但弊端也很明显,只适合检验某个条件相对关系是否满足现有关系,而不能直接列举出元素的相对关系,因为扩展域并查集只保存了元素具体种类的等价关系,而没有完整记录元素在关系集合中的相对关系,导致等价类之间是割裂的,没有直接相关性的。比如我想要知道动物 \(a\) 和 动物 \(b\) 的相对关系,就得先拿 \(a\) 的某个种类所在的等价类集合作为一个基准集合,再枚举 \(b\) 的所有种类(\(A,B,C\))是否处在这个基准集合,如果有关系则有且仅有一个具体种类处在基准集合进而判断其相对关系,而都没有处在基准集合说明 \(a\) 和 \(b\) 尚未建立关系。这个过程带权并查集能在合并和查询过程中直接实现,因此如果题目要求并不是检验条件矛盾这么简单的话,比如要求得知 \(a\) 和 \(b\) 的相对关系用以后续解题,那带权并查集会更加合适。

时间复杂度 \(O(k\log n)\)

空间复杂度 \(O(n)\) ,实际上是三倍空间

代码

方法一

#include <bits/stdc++.h>

using namespace std;

inline 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 << 3) + (x << 1) + (c ^ 48);c = getchar(); }///挪位加数
return x * f;
} int fa[50007], dist[50007]; int find(int x) {
if (fa[x] != x) {
int pre = fa[x];
fa[x] = find(pre);
dist[x] = (dist[x] + dist[pre]) % 3;
}
return fa[x];
} bool merge(int x, int y, int op) {
int rx = find(x);
int ry = find(y);
int delta = (dist[x] - dist[y] + 3) % 3;
if (rx == ry) return delta == op;
fa[rx] = ry;
dist[rx] = (op - delta + 3) % 3;
return true;
} int main() {
std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int n = read(), k = read();
for (int i = 1;i <= n;i++) fa[i] = i;
int ans = 0;
while (k--) {
int op = read(), x = read(), y = read();
op--;
if (x > n || y > n) ans++;
else if (!merge(x, y, op)) ans++;
}
cout << ans << '\n';
return 0;
}

方法二

#include <bits/stdc++.h>

using namespace std;

inline 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 << 3) + (x << 1) + (c ^ 48);c = getchar(); }///挪位加数
return x * f;
} int fa[150007]; int find(int x) {
return fa[x] == x ? x : fa[x] = find(fa[x]);
} void merge(int x, int y) {
fa[find(x)] = find(y);
} int main() {
std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int n = read(), k = read();
for (int i = 1;i <= 3 * n;i++) fa[i] = i;
int ans = 0;
while (k--) {
int op = read(), x = read(), y = read();
if (x > n || y > n)ans++;
else if (op == 1) {
if (find(x) == find(y + n) || find(x) == find(y + 2 * n)) ans++;
else merge(x, y), merge(x + n, y + n), merge(x + 2 * n, y + 2 * n);
}
else if (op == 2) {
if (find(x) == find(y + 2 * n) || find(x) == find(y)) ans++;
else merge(x, y + n), merge(x + n, y + 2 * n), merge(x + 2 * n, y);
}
}
cout << ans << '\n';
return 0;
}

NC16884 [NOI2001]食物链的更多相关文章

  1. NOI2001 食物链【扩展域并查集】*

    NOI2001 食物链 动物王国中有三类动物 A,B,C,这三类动物的食物链构成了有趣的环形.A 吃 B,B吃 C,C 吃 A. 现有 N 个动物,以 1 - N 编号.每个动物都是 A,B,C 中的 ...

  2. 洛谷 P2024 [NOI2001]食物链 解题报告

    P2024 [NOI2001]食物链 题目描述 动物王国中有三类动物 A,B,C,这三类动物的食物链构成了有趣的环形.A 吃 B,B 吃 C,C 吃 A. 现有 N 个动物,以 1 - N 编号.每个 ...

  3. 【题解】P2024 [NOI2001]食物链 - 数据结构 - 并查集

    P2024 [NOI2001]食物链 声明:本博客所有题解都参照了网络资料或其他博客,仅为博主想加深理解而写,如有疑问欢迎与博主讨论✧。٩(ˊᗜˋ)و✧*。 题目描述 动物王国中有三类动物 \(A,B ...

  4. NOI2001 食物链

    食物链 题目描述 动物王国中有三类动物 A,B,C,这三类动物的食物链构成了有趣的环形.A 吃 B,B 吃 C,C 吃 A. 现有 N 个动物,以 1 - N 编号.每个动物都是 A,B,C 中的一种 ...

  5. P2024 [NOI2001]食物链 并查集

    题目描述 动物王国中有三类动物 A,B,C,这三类动物的食物链构成了有趣的环形.A 吃 B,B 吃 C,C 吃 A. 现有 N 个动物,以 1 - N 编号.每个动物都是 A,B,C 中的一种,但是我 ...

  6. LG2024 [NOI2001]食物链

    拆点法 用并查集维护每种动物的同类.天敌.食物群 #include<cstdio> int fa[300005]; int n,k,ans; inline int read() { int ...

  7. 洛谷 P2024 [NOI2001]食物链 (并查集)

    嗯... 题目链接:https://www.luogu.org/problemnew/show/P2024 这道题和团伙这道题的思想比较类似,都是一个数组分成几个集合,但这道题的思路更加混乱,建议没做 ...

  8. NOI2001食物链

    描述 动物王国中有三类动物 A,B,C,这三类动物的食物链构成了有趣的环形.A吃B,B吃C,C吃A. 现有N个动物,以1-N编号.每个动物都是A,B,C中的一种,但是我们并不知道它到底是哪一种. 有人 ...

  9. [NOI2001] 食物链 (扩展域并查集)

    题目描述 动物王国中有三类动物 A,B,C,这三类动物的食物链构成了有趣的环形.A 吃 B,B 吃 C,C 吃 A. 现有 N 个动物,以 1 - N 编号.每个动物都是 A,B,C 中的一种,但是我 ...

随机推荐

  1. ONNX Runtime 源码阅读:Graph::SetGraphInputsOutputs() 函数

    目录 前言 正文 总结 前言 为了深入理解ONNX Runtime的底层机制,本文将对 Graph::SetGraphInputsOutputs() 的代码逐行分析. 正文 首先判断Graph是否从O ...

  2. 经典!服务端 TCP 连接的 TIME_WAIT 过多问题的分析与解决

    开源Linux 专注分享开源技术知识 本文给出一个 TIME_WAIT 状态的 TCP 连接过多的问题的解决思路,非常典型,大家可以好好看看,以后遇到这个问题就不会束手无策了. 问题描述 模拟高并发的 ...

  3. Node.js 中的进程和线程

    线程和进程是计算机操作系统的基础概念,在程序员中属于高频词汇,那如何理解呢?Node.js 中的进程和线程又是怎样的呢? 一.进程和线程 1.1.专业性文字定义 进程(Process),进程是计算机中 ...

  4. Android添加布局和按键

    Android添加布局和按键 Android布局方式分为 1.LinearLayout (线性布局) 2.ConstraintLayout (约束布局) 3.FrameLayout (帧布局) 4.T ...

  5. Oracle 19c单实例部署

    目录 Oracle 19c单实例部署: 1.配置yum: 2.安装rpm包: 3.设置hostname: 4.配置hostname解析: 5.配置时钟同步服务(ntp): 6.检查及配置内核参数: 7 ...

  6. 溢出属性,定位,z-index,JS

    溢出属性 1.visible(默认值):使溢出内容展示 2.hidden:隐藏溢出内容且不出现滚动条 3.scroll:隐藏溢出容器的内容,溢出的内容可以通过滚动呈现 4.auto:与scroll没啥 ...

  7. netty系列之:netty中常用的字符串编码解码器

    目录 简介 netty中的字符串编码解码器 不同平台的换行符 字符串编码的实现 总结 简介 字符串是我们程序中最常用到的消息格式,也是最简单的消息格式,但是正因为字符串string太过简单,不能附加更 ...

  8. npm 是什么?

    npm 是什么? 本文写于 2020 年 6 月 16 日 最近帮几个同学装开发环境,发现他们会各种"卡死"在 npm 安装一些包的过程中. 他们会非常纠结这个命令我明明敲的和网上 ...

  9. ubuntu 20.04 source mirror(aliyun)

    x64 deb http://mirrors.aliyun.com/ubuntu/ focal main restricted universe multiverse deb-src http://m ...

  10. CentOS配置epel源

    https://opsx.alibaba.com/mirror epel 配置方法 1.备份(如有配置其他epel源) mv /etc/yum.repos.d/epel.repo /etc/yum.r ...