poj 1182 经典并查集

Description
动物王国中有三类动物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),输出假话的总数。 


参考连接:点击打开链接

集合划分:

  注意,这里不是根据x与p[x]是否是同类来划分。而是根据“x与p[x]能否确定两者之间关系”来划分,若能确定x与p[x]关系,则它们同属一个集合

p[x]表示x根结点。r[x]表示p[x]与x关系。r[x]=0 表示p[x]与x同类;1表示p[x]吃x;2表示x吃p[x]。

  1. 当 d = 1的时候,( d - 1 ) = 0,也就是我们制定的意义 
  2.                 当 d = 2的时候,( d - 1 ) = 1,代表Y被X吃,也是我们指定的意义。 
  3.     所以,这个0,1,2不是随便选的 

路径压缩时的节点算法:r[x]=(r[x]+r[pre[x]])%3;

穷举证明:

        i  j
        爷爷 父亲 儿子 儿子与爷爷
           0 0 (i + j)%3 = 0
           0 1 (i + j)%3 = 1
           0 2 (i + j)%3 = 2
           1 0 (i + j)%3 = 1
           1 1 (i + j)%3 = 2
           1 2 (i + j)%3 = 0
           2 0 (i + j)%3 = 2
           2 1 (i + j)%3 = 0
           2 2 (i + j)%3 = 1
    嗯,这样可以看到,( 儿子relation + 父亲relation ) % 3 = 儿子对爷爷的relation

      这就是路径压缩的节点算法


将x和y所在集合连接起来:

这个公式,是分三部分,这么推出来的:
        ( d - 1 ) :这是X和Y之间的relation,X是Y的父节点时,Y的relation就是这个
        3 - relation[y] = 根据Y与根节点的关系,逆推根节点与Y的关系
            这部分也是穷举法推出来的,我们举例:
              0(父子同类)( 3 - 0 ) % 3 = 0
              1(父吃子) ( 3 - 1 ) % 3 = 2 //父吃子
              2(子吃父) ( 3 - 2 ) % 3 = 1 //子吃父


判断:

先处理特殊情况:      

    1.当x>n或y>n时,为假话(在这里竟然wa了一次...囧还是太不认真了)

     2.当d=2而x=y时,为假话

 
    其实所有的不同集合到最后都会被合并成一个集合的。我们只要在一个集合中找那些假话就可以了。

      (1)首先,如何判断
        1 X Y是不是假话。//此时 d = 1
        if ( X 和 Y 不在同一集合) Union(x,y,xroot,yroot,d)   

                    else if x.relation != y.relation ->假话

      (2)其次,如何判断
        2 X Y是不是假话 //此时d = 2
        if ( X 和 Y 不在同一集合)Union(x,y,xroot,yroot,d)
                    else (relation [y]+ 3 - relation[x] ) % 3 != 1 ->假话
        这个公式是这么来的:
          3 - relation[x]得到了根节点关于x的relation,
          relation [y]+ 3 - relation[x]得到了y关于x的relation
          所以,只要y关于x的relation不是1,就是y不被x吃的话,这句话肯定是假话!

      综合(1) 和(2),无论d=1或2,只要满足 ((relation[y]-relation[x]+3) Mod 3)<>(d-1) 即为假话



#include<iostream>
#include<cstring>
#include<string>
#include<cmath>
#include<cstdio>
#include<map>
#include<algorithm>
#define inf 0x3f3f3f3f
using namespace std;
int pre[50010];
int r[50010];
int findset(int x)
{
    if(pre[x]!=x)
    {
        int fy=findset(pre[x]);
        r[x]=(r[x]+r[pre[x]])%3;//路径最多为3层
        pre[x]=fy;
    }
    return pre[x];
}
bool union_set(int d,int x,int y)
{
    int fx=findset(x);
    int fy=findset(y);
    if(fx==fy)
    {
        if((3-r[x]+r[y])%3!=d-1)
            return 1;
        else
            return 0;
    }
    pre[fy]=fx;
    r[fy]=(r[x]+d-1+3-r[y])%3;
    return 0;
}
int main()
{
    int n,k;
    scanf("%d%d",&n,&k);
    int ans=0;
    for(int i=1;i<=n;i++)
        pre[i]=i;
    memset(r,0,sizeof(r));
    for(int t=1;t<=k;t++)
    {
        int d,x,y;
        scanf("%d%d%d",&d,&x,&y);
        if(x>n||y>n)
            ans++;
        else if(d==2&&x==y)
            ans++;
        else
        {
            if(union_set(d,x,y))
                ans++;
        }
    }
    cout<<ans<<endl;
    return 0;
}





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值