洛谷P2024 [NOI2001] 食物链 题解
题目链接:P2024 [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 句话有的是真的,有的是假的。当一句话满足下列三条之一时,这句话就是假话,否则就是真话。
- 当前的话与前面的某些真的话冲突,就是假话
- 当前的话中 X 或 Y 比 N 大,就是假话
- 当前的话表示 X 吃 X,就是假话
你的任务是根据给定的 N 和 K 句话,输出假话的总数。
$1 \le N \le 5\times10^4,~1 \le K \le 10^5$
考虑种类并查集。
我们维护三个并查集,分别存同类、食物和天敌
这里可以用1倍~3倍来划分,即 x
,x+n
,x+2*n
例如 x
和 x+n
合并,表示 x
及其同类均能吃 x+n
及其同类
不用A,B,C作为划分依据是因为题目没说每个动物的种类
并且在本题中,准确的种类并没有什么用处。
对于「X和Y是同类」,看上去只要把同类的并查集合并
但其实其他两个并查集也要合并,因为有可能X存在已知的天敌
而X和Y是同类,所以Y也有这个(些)天敌
同样的,对于「X吃Y」,也需要对两两关系进行处理
时间复杂度 $O(m)$
代码:
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <iomanip>
#include <random>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
#define N (int)(5e4+15)
int n,Q,f[N<<2];
void init(int n){for(int i=1; i<=n; i++) f[i]=i;}
int find(int x){return f[x]==x?x:f[x]=find(f[x]);}
void merge(int u,int v){f[find(u)]=find(v);}
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
// freopen("check.in","r",stdin);
// freopen("check.out","w",stdout);
cin >> n >> Q;
int res=0; init(3*n);
for(int op,x,y; Q--;)
{
cin >> op >> x >> y;
if(x>n||y>n){++res; continue;}
if(op==1)
{
if(find(x)==find(y+n)||find(x)==find(y+2*n)){++res; continue;}
merge(x,y); merge(x+n,y+n); merge(x+2*n,y+2*n);
}else
{
if(find(x)==find(y)||find(x)==find(y+n)){++res; continue;}
merge(x,y+2*n); merge(x+n,y); merge(x+2*n,y+n);
}
}
cout << res << '\n';
return 0;
}
参考文献