P2024 [NOI2001] 食物链

这是一个关于解决编程竞赛中的问题的描述,涉及到一个环形食物链的动物分类问题。给定动物编号和一系列关于同类和捕食关系的陈述,需要识别并计算出错误的陈述数量。错误包括陈述冲突、编号超出范围以及动物自食。解决方案利用并查集数据结构来处理动物间的关系。
摘要由CSDN通过智能技术生成

题目链接

luogu P2024

题面

题目描述

动物王国中有三类动物 A , B , C A,B,C A,B,C,这三类动物的食物链构成了有趣的环形。 A A A B B B B B B C C C C C C A A A
现有 N N N 个动物,以 1 ∼ N 1 \sim N 1N 编号。每个动物都是 A , B , C A,B,C A,B,C 中的一种,但是我们并不知道它到底是哪一种。
有人用两种说法对这 N N N 个动物所构成的食物链关系进行描述:

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

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

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

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

输入格式

第一行两个整数, N , K N,K N,K,表示有 N N N 个动物, K K K 句话。
第二行开始每行一句话(按照题目要求,见样例)

输出格式

一行,一个整数,表示假话的总数。

样例 #1

样例输入 #1

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

样例输出 #1

3

提示

对于全部数据, 1 ≤ N ≤ 5 × 1 0 4 1\le N\le 5 \times 10^4 1N5×104 1 ≤ K ≤ 1 0 5 1\le K \le 10^5 1K105

题意

n n n 只动物,动物们分为三种类型 ( A , B , C ) (A,B,C) (A,B,C),其中有 A A A B B B B B B C C C C C C A A A神奇关系(和石头剪刀布相类似)
现给出 m m m条语句,其格式为 P P P x x x y y y

  • P P P 1 1 1时,表示 x x x y y y的同类
  • P P P 2 2 2时,表示 x x x y y y

现求有多少条语句是谎言,谎言的定义如下:

  • x > n x>n x>n y > n y>n y>n
  • x x x y y y时, x = = y x==y x==y(即 x x x自己吃自己)
  • 与前面的语句相矛盾

当确定当前语句为谎言时,不执行当前语句
反之,执行当前语句

思路

这题其实是团伙的升级版,用的算法也是拓展域,只不过是由两个域变成了三个域
由于 A A A B B B B B B C C C C C C A A A的“环状”关系,我们可以将动物们的关系分三大类(同类、猎物、敌人)
分别表示为(假设当前的动物编号为 i i i

f a [ i ] fa[i] fa[i] → \to 同类
f a [ i + n ] fa[i+n] fa[i+n] → \to 猎物
f a [ i + 2 n ] fa[i+2n] fa[i+2n] → \to 天敌

然后我们就可以用类似团伙的做法进行并查集连接(对动物们指定“父亲”)
通过 f i n d find find数组就可以判断是否是同一个祖先,然后就可以进而判断关系的正确性


接下来我们来仔细分析一下如何去判断语句的正确性以及如何处理语句

x > n x>n x>n y > n y>n y>n 无聊判断输入越界
x x x y y y的同类 ( P = = 1 ) (P==1) (P==1)(假设状态)

如果 x x x y y y的猎物 或 x x x y y y的天敌 成立,这就会推翻 x x x y y y的同类 这个假设,判断为谎言
否则就证实了 x x x y y y的同类 这个假设,接下来就可以进行关系的连接了
因为 x x x y y y的同类,所以 x x x的猎物 自然也就是 y y y的猎物、 x x x的天敌 也就是 y y y的天敌

x x x y y y ( P = = 2 ) (P==2) (P==2)

如果 x = = y x==y x==y(即自己吃自己)成立,判断为谎言
如果 x x x y y y的同类 或 x x x y y y的猎物(等价于 x x x的天敌是 y y y)成立,这就会推翻 x x x y y y 这个假设,判断为谎言
否则就证实了 x x x y y y 这个假设,接下来就可以进行关系的连接了
因为 x x x y y y,所以 x x x的猎物是 y y y x x x y y y的天敌、 x x x的天敌是 y y y的猎物

为什么 x x x的天敌是 y y y的猎物呢?
其实这个细节我一开始也没想到,其实只要在图纸上画一下就明白了
在这里插入图片描述
假设 x x x A A A类动物、 y y y B B B类动物、 z z z C C C类动物
x x x y y y y y y z z z z z z x x x
我们可以从图中提取出以下信息:

  1. y y y的猎物是 z z z
  2. x x x的天敌是 z z z

所以 y y y的猎物是 x x x的天敌( x x x的天敌是 y y y的猎物)
代码实现细节看代码注释

#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,k,orz,x,y,ans,fa[50010*3];
//fa[1~n]为同类域 
//fa[n+1~2n]为猎物域
//fa[2n+1~3n]为天敌域
int find(int x){//并查集 find"父亲" 
	if(fa[x]!=x)fa[x]=find(fa[x]);
	return fa[x];
}
void merge(int x,int y){//并查集连接 
	fa[find(x)]=find(y);
	return;
}
signed main(){
	scanf("%lld%lld",&n,&k);
	for(int i=1;i<=3*n;i++)fa[i]=i;//初始化 
	while(k--){
		scanf("%lld%lld%lld",&orz,&x,&y);
		if(x>n||y>n)ans++;//x和y的越界特判 
		else if(orz==1){
			//假设x是y的同类 
			if(find(x)==find(y+n)||find(x)==find(y+2*n))ans++;
			//如果x是y的猎物或x是y的天敌在前面的语句出现过,则判断为谎话 
			else{
				//x是y的同类成立 
				merge(x,y);//x的同类是y 
				merge(x+n,y+n);//x的猎物是y的猎物 
				merge(x+2*n,y+2*n);//x的天敌是y的天敌 
			}
		}else{
			//假设x吃y 
			if(x==y||find(x)==find(y)||find(x)==find(y+n))ans++;
			//如果x吃y,又x==y,则是x自己吃自己,判断为谎话
			//如果x是y的同类 或 x是y的猎物,则与x吃y(x是y的天敌)相矛盾,判断为谎话
			else{
				//x吃y成立 
				merge(x+n,y);//x的猎物是y 
				merge(x+2*n,y+n);//x的天敌是y的猎物(重点,思路里有证明) 
				merge(x,y+2*n);//x是y的天敌
			}
		}
	}
	printf("%lld",ans);
	return 0;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值