P2024 食物链
题目描述
动物王国中有三类动物 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 句话,输出假话的总数。
输入输出格式
输入格式:从 eat.in 中输入数据
第一行两个整数,N,K,表示有 N 个动物,K 句话。
第二行开始每行一句话(按照题目要求,见样例)
输出格式:输出到 eat.out 中
一行,一个整数,表示假话的总数。
输入输出样例
说明
1 ≤ N ≤ 5 ∗ 10^4
1 ≤ K ≤ 10^5
=======================================
emm......这题算是一种新题型吧,确实不看题解的话自己这种蒟蒻很难想到开3倍并查集的方法。
三倍的并查集,1~n表示动物间是否属于同一类;n+1~2n表示一个食物集合,即如果1吃2,则将1~n中的1与n+1~2n中的2相连;同理,2n+1~3n表示一个天敌集合。
然后,对于每一句话的判断,如果为“1”类型,需要判断两种动物间是否具有捕食关系。(因为后面对“a吃b,b吃c,c自然吃a”的特殊处理,所以在判断捕食关系时均用1~n中元素作为捕食者,n+1~2n中元素作为食物);若为“2”类型,例如a吃b,则需要排除①a、b同类 ②b吃a 的情况,前者无疑在1~n的集合中判断,而后者仍然要取1~n中元素作为捕食者,n+1~2n中元素作为食物。
另外,对于a吃b、b吃c、然后c自然吃a,所以连接c(1~n)、a(n+1~2n)的处理为什么是连接a(n+1~2n),b(2n+1~3n),画一下图就可以看出来了,确实挺巧妙的。
所以综上所述,在实现过程中,判断是否同类是在集合1~n中进行,判断捕食关系是在集合1~n和集合n+1~2n中进行。
最后,如果该句话错误,ans自然加一,但如果正确,对于“1”类型句子,需要合并集合对应两个动物序号,且3部分集合中均需要合并,因为假设a在食物集合中有食物b,那么食物集合中与b同类的动物d也要与捕食者a相连;而对于“2”类型,若错误,ans++,反之正确,则集合1中捕食者与集合2中食物相连、集合1中食物与集合三中天敌相连、另外集合2中捕食者与集合3中食物相连(代表食物的食物是捕食者的天敌......画下图就好理解了)。
参考题解:https://blog.csdn.net/qq_39553725/article/details/76474124
AC代码:
#include<bits/stdc++.h>
using namespace std;
int fat[50001*3];
int father(int x)
{
while(x!=fat[x])x=fat[x];
return x;
}
void unionn(int x,int y)
{
fat[father(x)]=father(y);
}
int ans=0;
int main()
{
//freopen("1.txt","r",stdin);
int n,k;
cin>>n>>k;
for(int i=1;i<=n*3;++i)fat[i]=i;
for(int i=1;i<=k;++i)
{
int flag;
int x,y;
cin>>flag>>x>>y;
if(flag==1)
{
if(x>n||y>n)ans++;
else
{
int fa=father(x),fb=father(y+n),fc=father(y),fd=father(x+n);
if(fa==fb||fd==fc)ans++;
else
{
unionn(x,y);
unionn(x+n,y+n);
unionn(x+2*n,y+2*n);
}
}
}
else if(flag==2)
{
if(x>n||y>n)ans++;
else if(x==y)ans++;
else
{
int fa=father(x),fb=father(y);
if(fa==fb)ans++;
else
{
int fa2=father(x+n),fb2=father(y);
if(fa2==fb2)ans++;
else
{
unionn(x,y+n);
unionn(x+2*n,y);
unionn(x+n,y+2*n);
}
}
}
}
}
cout<<ans<<endl;
return 0;
}
The end;