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\n",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\n",cnt);

    return 0;
}
View Code

猜你喜欢

转载自www.cnblogs.com/bxd123/p/10731742.html
今日推荐