• P2024 [NOI2001]食物链 并查集


    题目描述

    动物王国中有三类动物 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

    很明显是并查集  但是又很难下手  不知道该和同类并还是和敌对关系并。。。

    有一个非常巧妙地方法   将并查集扩为3*n  1-n表示本体  n+1-2*n表示猎物  2*n+1-3*n表示天敌 所有有关系的全部并一下!

    一开始的代码  一直wa

    #include<bits/stdc++.h>
    using namespace std;
    //input by bxd
    #define rep(i,a,b) for(int i=(a);i<=(b);i++)
    #define repp(i,a,b) for(int i=(a);i>=(b);--i)
    #define RI(n) scanf("%d",&(n))
    #define RII(n,m) scanf("%d%d",&n,&m)
    #define RIII(n,m,k) scanf("%d%d%d",&n,&m,&k)
    #define RS(s) scanf("%s",s);
    #define ll long long
    #define REP(i,N)  for(int i=0;i<(N);i++)
    #define CLR(A,v)  memset(A,v,sizeof A)
    //////////////////////////////////
    #define inf 0x3f3f3f3f
    #define N 100000+5
    
    int f[3*N];
    int find1(int x)
    {
        return x==f[x]?x:f[x]=find1(f[x]);
    }
    void union1(int a,int b)
    {
        int x=find1(a);
        int y=find1(b);
        if(x!=y)
            f[x]=y;
    }
    int main()
    {
        int n,m;
        RII(n,m);
        rep(i,1,3*n)f[i]=i;
    
        int cnt=0;
        rep(i,1,m)
        {
            int a,b,c;
            RIII(a,b,c);
            if( b>n||c>n||(a==2&&b==c))
            {
                cnt++;continue;
            }
            if(a==1)
            {
                if(find1(2*c)!=find1(b)&&find1(3*c)!=find1(b))
                {
                    union1(c,b);
                }
                else cnt++;
            }
            else if(a==2)
            {
                if(find1(b)==find1(c))
                {
                    cnt++;continue;
                }
                if(find1(3*b)==find1(c)||find1(2*c)==find1(b))
                {
                    cnt++;continue;
    
                }
                union1(2*b,c);
                union1(3*c,b);
                union1(2*c,3*b);
                //union1()
            }
        }
        printf("%d
    ",cnt);
    
    
    
    
        return 0;
    }
    View Code

    +kn 写成*k 我不wa谁wa。。

    还有一个细节就是当是同类的时候  也要union 3对点 

    还有一个容易漏的是   因为是三角循环  除XY另外一个类也要进行操作

    #include<bits/stdc++.h>
    using namespace std;
    //input by bxd
    #define rep(i,a,b) for(int i=(a);i<=(b);i++)
    #define repp(i,a,b) for(int i=(a);i>=(b);--i)
    #define RI(n) scanf("%d",&(n))
    #define RII(n,m) scanf("%d%d",&n,&m)
    #define RIII(n,m,k) scanf("%d%d%d",&n,&m,&k)
    #define RS(s) scanf("%s",s);
    #define ll long long
    #define REP(i,N)  for(int i=0;i<(N);i++)
    #define CLR(A,v)  memset(A,v,sizeof A)
    //////////////////////////////////
    #define inf 0x3f3f3f3f
    #define N 100000+5
    
    int f[3*N];
    int find1(int x)
    {
        return x==f[x]?x:f[x]=find1(f[x]);
    }
    void union1(int a,int b)
    {
        int x=find1(a);
        int y=find1(b);
        if(x!=y)
            f[x]=y;
    }
    int main()
    {
        int n,m;
        RII(n,m);
        rep(i,1,3*n)f[i]=i;
        int cnt=0;
        rep(i,1,m)
        {
            int a,b,c;
            RIII(a,b,c);
            if( b>n||c>n||(a==2&&b==c))
            {
                cnt++;continue;
            }
            if(a==1)
            {
                if(find1(n+c)==find1(b)||find1(2*n+c)==find1(b))
                {
                    cnt++;continue;
                }
                union1(b,c);
                union1(b+n,c+n);
                union1(b+2*n,c+2*n);
            }
            else if(a==2)
            {
                if(find1(2*n+b)==find1(c)||find1(b)==find1(c))
                {
                    cnt++;continue;
                }
                union1(n+b,c);
                union1(2*n+c,b);
                union1(n+c,2*n+b);
            }
        }
        printf("%d
    ",cnt);
    
        return 0;
    }
    View Code
  • 相关阅读:
    【算法笔记】B1020 月饼
    JZOJ 3412. 【NOIP2013模拟】KC看星
    JZOJ 3517. 空间航行
    JZOJ 3515. 软件公司
    JZOJ 3514. 最小比例
    JZOJ 3490. 旅游(travel)
    luogu P3178 [HAOI2015]树上操作
    JZOJ 3427. 归途与征程
    JZOJ 3426. 封印一击
    JZOJ 3425. 能量获取
  • 原文地址:https://www.cnblogs.com/bxd123/p/10731742.html
Copyright © 2020-2023  润新知