洛谷 P2024 食物链(扩展域并查集)

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:  复制
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 ∗ 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;



猜你喜欢

转载自blog.csdn.net/qq_41661919/article/details/80220175
今日推荐