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;
    }
    
  • 相关阅读:
    支持向量机SVM知识点概括
    决策树知识点概括
    HDU 3081 Marriage Match II
    HDU 3572 Task Schedule
    HDU 4888 Redraw Beautiful Drawings
    Poj 2728 Desert King
    HDU 3926 Hand in Hand
    HDU 1598 find the most comfortable road
    HDU 4393 Throw nails
    POJ 1486 Sorting Slides
  • 原文地址:https://www.cnblogs.com/MisakaMKT/p/11252283.html
Copyright © 2011-2022 走看看