C++食物链【NOI2001】 并查集+建虚点

B. 食物链【NOI2001】
内存限制:256 MiB
时间限制:1000 ms
标准输入输出
题目类型:传统
评测方式:文本比较
题目描述
动物王国中有三类动物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句话有的是真的,有的是假的。当一句话满足下列三条之一时,这句话就是假话,否则就是真话。

1)当前的话与前面的某些真的话冲突,就是假话;

2)当前的话中X或Y比N大,就是假话;

3)当前的话表示X吃X,就是假话。

你的任务是根据给定的N(1≤ N ≤50,000)和K句话(0≤K≤100,000),输出假话的总数。

输入格式
第一行是两个整数N和K,以一个空格分隔。

以下K行每行是三个正整数 D,X,Y,两数之间用一个空格隔开,其中D表示说法的种类。

若D=1,则表示X和Y是同类。

若D=2,则表示X吃Y。

输出格式
只有一个整数,表示假话的数目。

样例
样例输入
100 7
1 101 1
2 1 2
2 2 3
2 3 3
1 1 3
2 3 1
1 5 5
样例输出
3
数据范围与提示
100 7 1 101 1 假话 2 1 2 真话 2 2 3 真话 2 3 3 假话 1 1 3 假话 2 3 1 真话 1 5 5 真话

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

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

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

对于单个点\(A_{1}\),我们可以给它建两个虚点\(A_{2},A_{3}\),并且假设出这三个点之间的关系,\(A_{1}\)\(A_{2}\)\(A_{2}\)\(A_{3}\)\(A_{3}\)\(A_{1}\)。同样在假设有一个点\(B_{1}\),它同样也有这些虚点。我们可以使用一个有向图来表示这个关系以方便理解。

在这里插入图片描述
\(A_{1}\)可以吃掉\(B_{1}\)时,我们可以发现\(A_{1}\)\(B_{3}\)成了同类(参考定理1,同样根据定理1,我们还可以得出\(A_{2}\)\(B_{1}\)是同类,如果是\(B_{1}吃A_{1}则相反\))。

在这里插入图片描述
根据定理2,我们可以得出\(B_{1}\)可以吃掉\(A_{3}\),同时它也可以吃掉\(B_{2}\),所以\(A_{3}\)\(B_{2}\)也是同类..
在这里插入图片描述
如果我们判断两个点是同类,则可以将他们放入同一个并查集中。需要注意的是,虚点只是用来帮助我们判断动物之间的关系的,并没有实际含义。

那么如何判断一句话是矛盾的呢,我们可以分情况讨论。
如果\(A_{1}\)可以吃\(B_{1}\),结合图片,我们可以发现\(A_{1}\)\(B_{2}\),依此类推\(B_{1}\)\(A_{3}\)也不是同类,\(A_{1}\)\(B_{1}\)更不是同类。我们便可以使用并查集,如果上述两者在同一个并查集,便说明它们是矛盾的。

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

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

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

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

的区别。

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

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

代码

#include <iostream>
#include <cstdio>
#include <string>
#include <map>
using namespace std;

#define N 50010

int fa[N*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,b,c;i<=k;i++) {
        cin>>a>>b>>c;
        if(b>n || c>n || (a==2 && b==c)) { ans++; continue; }
        if(a==2) {
            if(find(b)==find(c)||find(b+n*2)==find(c)||find(c+n)==find(b)) ans++;
            else {
                fa[find(b+n)]=find(c);
                fa[find(b)]=find(c+2*n);
                fa[find(c+n)]=find(b+2*n);
            }
        }
        if(a==1) {
            if((find(b+n) == find(c)) ||(find(b+2*n) == find(c))) ans++;
            else {
                fa[find(b+2*n)]=find(c+2*n);
                fa[find(b+n)]=find(c+n);
                fa[find(b)]=find(c);
            }
        }
    }
    cout<<ans;
}
posted @ 2019-07-26 19:09  MisakaMKT  阅读(677)  评论(0编辑  收藏  举报