Fork me on GitHub

食物链の t j

食物链

  首先考虑题目中所说的 3 种矛盾情况中的后两种,都很好判断,关键是如何判断当前的话是与前面的话冲突的。

  这里我们先给出两个定理以方便判断一些关系:

一。因为三个种族都有一个且且仅有一个能吃的种族。如果动物 A 能吃 B,动物 C 也能吃 B,那么说明A 和 C 便是同一个种族,否则这个关系就是矛盾的。 二。如果 A 能吃 B,B 能吃 C,那么可以得出 C 能吃 A,也就是如下这个关系: 

在这里插入图片描述

  对于单个点A1,我们可以给它建两个虚点A2,A3,并且假设出这三个点之间的关系,A1吃 A2,A2吃 A3,A3吃A1。同样在假设有一个点B1,它同样也有这些虚点。我们可以使用一个有向图来表示这个关系以方便理解。

在这里插入图片描述 

  当A1可以吃掉B1 时,我们可以发现A1和 B3 成了同类(参考定理 1,同样根据定理 1,我们还可以得出A2和B1是同类,如果是B1吃A1则相反)。

在这里插入图片描述 

  根据定理 2,我们可以得出B1可以吃掉A3,同时它也可以吃掉B2,所以A3 和 B2  也是同类..

 如果我们判断两个点是同类,则可以将他们放入同一个并查集中。需要注意的是,虚点只是用来帮助我们判断动物之间的关系的,并没有实际含义。

那么如何判断一句话是矛盾的呢,我们可以分情况讨论。 如果A1 可以吃B1 ,结合图片,我们可以发现A1和B2,依此类推 B1 和A3也不是同类,A1和 B1更不是同类。我们便可以使用并查集,如果上述两者在同一个并查集,便说明它们是矛盾的。

同理如果A1 和B1是同类的话也可以这么考虑。需要注意的是,所以情况都要考虑完,实点和虚点入并查集时情况也要考虑完(不然就是 WA,XD)。

可能有些同学要问为什么要判断两个点在同一个并查集,来判断这句话是不是矛盾的,而不是判断两个点不在同一个并查集,也就是

if(find(A1) == find(B2)) 矛盾

if(find(A3) != find(B2)) 矛盾

的区别。

因为我们建立的是虚点,一开始所有的点都在独立的并查集,直接判断便会误判。 可以这么理解,我们在条件不够的情况下,第二种方式是 “猜测”,第一种方式则是 “尽量满足关系,最后不得不判断矛盾”。

关于建立虚点,设当前有 n 个实点,A的两个虚点可以用 A+n和 A+2*n来表示.

 #include<bits/stdc++.h>
using namespace std;
int fa[50005*3],n,k,ans;
int find(int x) {
    if(fa[x]!=x) return fa[x]=find(fa[x]);
    return x;
}
int main() {
    cin>>n>>k;
    for(int i=1;i<=n*3;i++) fa[i]=i;
    for(int i=1,a,x,y;i<=k;i++) {
        scanf("%d%d%d",&a,&x,&y);
        if(x>n || y>n || (a==2 && x==y)) { ans++; continue; }
        if(a==1) {
            if((find(x+n) == find(y)) ||(find(x+2*n) == find(y))) 
				ans++;
            else {
                fa[find(x+2*n)]=find(y+2*n);
                fa[find(x+n)]=find(y+n);
                fa[find(x)]=find(y);
            }
        }
        if(a==2) {
            if(find(x)==find(y)||find(x+n*2)==find(y)||find(y+n)==find(x)) ans++;
            else {
                fa[find(x+n)]=find(y);
                fa[find(x)]=find(y+2*n);
                fa[find(y+n)]=find(x+2*n);
            }
        }
    }
    printf("%d",ans);
    return 0;
}

 

posted @ 2022-07-30 16:18  Doria_tt  阅读(45)  评论(0编辑  收藏  举报