NC16884 [NOI2001]食物链
题目
题目描述
动物王国中有三类动物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]食物链的更多相关文章
- NOI2001 食物链【扩展域并查集】*
NOI2001 食物链 动物王国中有三类动物 A,B,C,这三类动物的食物链构成了有趣的环形.A 吃 B,B吃 C,C 吃 A. 现有 N 个动物,以 1 - N 编号.每个动物都是 A,B,C 中的 ...
- 洛谷 P2024 [NOI2001]食物链 解题报告
P2024 [NOI2001]食物链 题目描述 动物王国中有三类动物 A,B,C,这三类动物的食物链构成了有趣的环形.A 吃 B,B 吃 C,C 吃 A. 现有 N 个动物,以 1 - N 编号.每个 ...
- 【题解】P2024 [NOI2001]食物链 - 数据结构 - 并查集
P2024 [NOI2001]食物链 声明:本博客所有题解都参照了网络资料或其他博客,仅为博主想加深理解而写,如有疑问欢迎与博主讨论✧。٩(ˊᗜˋ)و✧*。 题目描述 动物王国中有三类动物 \(A,B ...
- NOI2001 食物链
食物链 题目描述 动物王国中有三类动物 A,B,C,这三类动物的食物链构成了有趣的环形.A 吃 B,B 吃 C,C 吃 A. 现有 N 个动物,以 1 - N 编号.每个动物都是 A,B,C 中的一种 ...
- P2024 [NOI2001]食物链 并查集
题目描述 动物王国中有三类动物 A,B,C,这三类动物的食物链构成了有趣的环形.A 吃 B,B 吃 C,C 吃 A. 现有 N 个动物,以 1 - N 编号.每个动物都是 A,B,C 中的一种,但是我 ...
- LG2024 [NOI2001]食物链
拆点法 用并查集维护每种动物的同类.天敌.食物群 #include<cstdio> int fa[300005]; int n,k,ans; inline int read() { int ...
- 洛谷 P2024 [NOI2001]食物链 (并查集)
嗯... 题目链接:https://www.luogu.org/problemnew/show/P2024 这道题和团伙这道题的思想比较类似,都是一个数组分成几个集合,但这道题的思路更加混乱,建议没做 ...
- NOI2001食物链
描述 动物王国中有三类动物 A,B,C,这三类动物的食物链构成了有趣的环形.A吃B,B吃C,C吃A. 现有N个动物,以1-N编号.每个动物都是A,B,C中的一种,但是我们并不知道它到底是哪一种. 有人 ...
- [NOI2001] 食物链 (扩展域并查集)
题目描述 动物王国中有三类动物 A,B,C,这三类动物的食物链构成了有趣的环形.A 吃 B,B 吃 C,C 吃 A. 现有 N 个动物,以 1 - N 编号.每个动物都是 A,B,C 中的一种,但是我 ...
随机推荐
- 5个容易忽视的PostgreSQL查询性能瓶颈
PostgreSQL 查询计划器充满了惊喜,因此编写高性能查询的常识性方法有时会产生误导.在这篇博文中,我将描述借助 EXPLAIN ANALYZE 和 Postgres 元数据分析优化看似显而易见的 ...
- Python学习之路——类-面向对象编程
类 面向对象编程 通过类获取一个对象的过程 - 实例化 类名()会自动调用类中的__init__方法 类和对象之间的关系? 类 是一个大范围 是一个模子 它约束了事务有哪些属性 但是不能约束具体的值 ...
- xpath & csv文件读写
原理:拿到网页源代码并且进行分析 关键词:etree .xpath a[@href="dapao"] a/@href text() impo ...
- Halo 开源项目学习(七):缓存机制
基本介绍 我们知道,频繁操作数据库会降低服务器的系统性能,因此通常需要将频繁访问.更新的数据存入到缓存.Halo 项目也引入了缓存机制,且设置了多种实现方式,如自定义缓存.Redis.LevelDB ...
- Linux-I/O模型详解
I/O介绍 I/O通常有内存IO.网络I/O.磁盘I/O等,但我们通常说的是网络I/O以及磁盘I/O.网络I/O:本质是socket读取 每次I/O请求,都会有两个阶段组成: 第一步:等待数据,即数据 ...
- MySQL存储过程入门了解
0.环境说明: mysql版本:5.7 1.使用说明 存储过程是数据库的一个重要的对象,可以封装SQL语句集,可以用来完成一些较复杂的业务逻辑,并且可以入参出参(类似于java中的方法的书写). ...
- 羽夏 Bash 简明教程(上)
写在前面 该文章根据 the unix workbench 中的 Bash Programming 进行汉化处理并作出自己的整理,并参考 Bash 脚本教程 和 BashPitfalls 相关内容 ...
- 1.Spring开发环境搭建——intellj
1.在intellj中新建项目,选择JDK版本(1.8版本) 2.选择相关信息填写,注意Java版本要和上面步骤选择的版本一致. 3.选择springBoot版本,勾选Spring Web选项. 4. ...
- 929. Unique Email Address - LeetCode
Question 929. Unique Email Address Solution 题目大意: 给你一个邮箱地址的数组,求出有多少个不同的地址,其中localName有如下规则 加号(+)后面的字 ...
- Android 12(S) 图像显示系统 - GraphicBuffer同步机制 - Fence
必读: Android 12(S) 图像显示系统 - 开篇 一.前言 前面的文章中讲解Android BufferQueue的机制时,有遇到过Fence,但没有具体讲解.这篇文章,就针对Fence这种 ...