zoukankan      html  css  js  c++  java
  • 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;
    }
    
  • 相关阅读:
    OSGI概念理解和入门
    东莞工厂探访
    软件开发经验之谈
    参加2019华为软件精英挑战赛心得
    论python中的函数参数的传递问题。
    pyhton中常用的基础模块
    程序编译
    hadoop源码学习(二)之ZooKeeper
    我用linux系统的采坑记
    macos解决Hadoop之Unable to load native-hadoop library
  • 原文地址:https://www.cnblogs.com/MisakaMKT/p/11252283.html
Copyright © 2011-2022 走看看