食物链

题目描述

动物王国中有三类动物 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 句话,输出假话的总数。

输入输出格式

输入格式:

第一行两个整数,N,K,表示有 N 个动物,K 句话。

第二行开始每行一句话(按照题目要求,见样例)

输出格式:

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


说明

1 ≤ N ≤ 5 ∗ 10^4

1 ≤ K ≤ 10^5


输入输出样例: 
100 7
1 101 1
2 1 2
2 2 3
2 3 3
1 1 3
2 3 1
1 5 5
3





























这道题是怎么回事呢?我们仔细读题就会发现,每个动物都有三种身份:1.自己(同类)  2.自己的食物  3.自己的天敌
然后就是另一个很尴尬的问题,也是并查集的难点:怎么合理储存两个动物之间的关系?
在这之前,我们由于前面的三种不用的身份,int一个大小为3 * n的数组fa[maxn]
所以对应任何一个动物i

fa[i]  -->  自己和同类
fa[i + n]  -->  自己的食物
fa[i + 2 * n]  -->  自己的天敌

所以直接推理一下:
如果两个动物半毛钱关系都没有,那么他们无论是fa[i]、fa[i + n]、 fa[i + 2 * n]的祖先绝对不可能一样。
如果两个动物发生了关系233...., 那么他们的三个祖先互相一定存在相等的关系。
如果两个动物是一个类型的,那么他们的三个祖先也就一样了呀。
顺着这个逻辑推下去,再结合一下我这个蒟蒻写的代码,应该就明白了呀.....

所以...贴一发我自己的个性代码:


#include<cstdio>

using namespace std;

const int maxn = 5e4 + 5;
int n, k; 
int a, b, c;
int ans = 0;
int aaa1, bbb1, aaa2, bbb2, aaa3, bbb3;  // 分别对应的三个祖先 
int fa[150005];		//  三倍大的数组 

inline int find(int t)   //   标准找爸爸操作 
{
	if(fa[t] == t)	return t;
	return fa[t] = find(fa[t]);
}  


inline void workk_same()
{
	aaa1 = find(b);
	bbb1 = find(c);
	aaa2 = find(b + n);
	bbb2 = find(c + n);
	aaa3 = find(b + 2 * n);
	bbb3 = find(c + 2 * n);
	
	// 直接找出三个老祖宗 
	
	if(aaa1 == bbb2  || aaa1 == bbb3)
	{
		ans++;
		return;
	}
	// 判定有没有符合要求 
	
	fa[aaa1] = bbb1;
	fa[aaa2] = bbb2;
	fa[aaa3] = bbb3;
	
	//   合并 
}

inline void workk_b_eat_c()
{
	if(b == c)	
	{
		ans++;
		return;
	}
	aaa1 = find(b);
	bbb1 = find(c);
	aaa2 = find(b + n);
	bbb2 = find(c + n);
	aaa3 = find(b + 2 * n);
	bbb3 = find(c + 2 * n);
	if(aaa1 == bbb1 || aaa1 == bbb2 || aaa2 == bbb3 || aaa2 == bbb2 || aaa3 == bbb3 || aaa3 == bbb1)
	{
		ans++;
		return;
	}				//  判定是否合理 
	
	
	fa[aaa1] = bbb3;
	fa[aaa2] = bbb1;
	fa[aaa3] = bbb2;    //  合并 
}

int main()
{
	scanf("%d%d", &n, &k);
	for(int i = 1; i <= 3*n; ++i)
		fa[i] = i;
	for(int i = 1; i <= k; ++i)
	{
		scanf("%d%d%d", &a, &b, &c);
		if(b > n || c > n)
		{
			ans++;
			continue;
		}
		if(a == 1)	workk_same();
		else workk_b_eat_c();
	}
	printf("%d", ans);  
	return 0;	//  程序再见! 
}




根据引用和引用的描述,动物王国中有三类动物A、B、C,它们的食物链构成了一个有趣的环形。具体来说,A吃B,B吃C,C吃A。现在有N个动物,每个动物都是A、B、C中的一种,但我们不知道它们到底属于哪一类。有人用两种说法对这N个动物之间的食物链关系进行描述,一种是"1 X Y",***根据以下三个条件,我们可以判断一句话是否为假话: 1)当前的话与前面的某些真话冲突; 2)当前的话中的X或Y比N大; 3)当前的话表示X吃X。 通过判断这些条件,我们可以计算出假话的总数。 在解决这个问题时,可以使用并查集来高效地维护动物之间的关系,并快速判断是否产生了矛盾。具体而言,对于每一只动物i,我们创建3个元素i-A、i-B、i-C,并用这3*N个元素构建并查集。这个并查集可以维护以下信息: 1)i-x表示"i属于种类x"; 2)并查集中的每一个组表示组内所有元素代表的情况都同时发生或不发生。 例如,如果i-A和j-B在同一个组里,就表示如果i属于种类A,那么j一定属于种类B;如果j属于种类B,那么i一定属于种类A。因此,对于每一条信息,我们只需要按照以下操作进行: 1)第一种说法,即x和y属于同一种类——合并x-A和y-A、x-B和y-B、x-C和y-C; 2)第二种说法,即x吃y——合并x-A和y-B、x-B和y-C、x-C和y-A。 在执行合并之前,需要先判断合并是否会产生矛盾。例如,在第一种说法的情况下,需要检查x-A和y-B、y-C是否在同一组中等信息。 另外,初始化时每个元素的祖先都是自己,这样可以避免出现相同的关系。 以上是解决这个问题的一种方法,具体的实现细节可能需要根据实际情况进行调整。希望对你有所帮助!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值