并查集-食物链(向量偏移)

http://poj.org/problem?id=1182

食物链
Time Limit: 1000MS   Memory Limit: 10000K
Total Submissions: 44998   Accepted: 13122

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),输出假话的总数。 

Input

第一行是两个整数N和K,以一个空格分隔。 
以下K行每行是三个正整数 D,X,Y,两数之间用一个空格隔开,其中D表示说法的种类。 
若D=1,则表示X和Y是同类。 
若D=2,则表示X吃Y。

Output

只有一个整数,表示假话的数目。

Sample Input

100 7
1 101 1 
2 1 2
2 2 3 
2 3 3 
1 1 3 
2 3 1 
1 5 5

Sample Output

3
分析:该题是种类并查集,每个动物都属于A,B,C三类中的一类,始终满足A<-吃-B<-吃-C<-吃-A的关系;

用两个数组即可,f[]表示祖先节点,mark[]表示每个节点的状态

所以用0,1,2,三个数表示与祖先节点的关系:0表示与祖先同类,1表示被祖先吃,2表示吃祖先;

举个例子就很清楚了

设被吃者指向父节点即吃者

112:

A=1的祖先是1此时x=f[1]=1, mark[1]=0;

B=2的祖先是2此时y=f[2]=2], mark[2]=0;

因为12,所以f[y]=x即:f[2]=1; 2的状态比1

所以mark[y]=(mark[a]+1-mark[b]+3)%3;(就是把当前的增量平移到祖先身上)

==>mark[1]=0;mark[2]=1;

(2) 23

   A=2的祖先是1此时x=f[2]=1mark[2]=1;

   B=3的祖先是3此时y=f[3]=3;  mark[3]=0;

   f[y]=x;即:f[3]=1;

所以mark[y]=(mark[a]+1-mark[b]+3)%3;

==>mark[3]=2;

此时图形是:


(3)同理还有45,56可构成下图:


(4)此时加入35

A=3,x=f[3]=1;mark[3]=2;

B=5;y=f[5]=4;mark[5]=1;

3的状态要比5的状态小1,所以看看要是把5集合加入到3的集合中,5的祖先平移 量是多少mark[y]=(mark[a]+1-mark[b]+3)%3,因为mark[y]在任何时候都是mark[y]=0;所以直接赋值就行了,此时:


此时只是更新了祖先的mark值;

当找5的时候务必会进行一次finde()函数:

int finde(int x)

{

    if(x!=f[x])

    {

        int t=f[x];

        f[x]=finde(f[x]);

        mark[x]=(mark[x]+mark[t])%3;

    }

    return f[x];

}

F[5]=1;mark[5]=0;

同理要求6的话f[6]=1;mark[6]=1;



可以看出mark值相同的动物是一类的;

加入35是同类的话

Mark[y]=(mark[a]-mark[b]+3)%3;

Mark[4]=1;mark[5]=2;mark[6]=0;


程序:

#include"string.h"
#include"stdio.h"
#include"queue"
#include"stack"
#include"iostream"
#include"math.h"
#define M 60006
#define inf 100000000
using namespace std;
int f[M],mark[M];
int finde(int x)
{
    if(x!=f[x])
    {
        int t=f[x];
        f[x]=finde(f[x]);
        mark[x]=(mark[x]+mark[t])%3;
    }
    return f[x];
}
void make(int a,int b)
{
    int x=finde(a);
    int y=finde(b);
    if(x!=y)
    {
        f[y]=x;
        mark[y]=(3+mark[a]+1-mark[b])%3;
    }
}
void same(int a,int b)
{
    int x=finde(a);
    int y=finde(b);
    if(x!=y)
    {
        f[y]=x;
        mark[y]=(3+mark[a]-mark[b])%3;
    }
}
int main()
{
    int n,m,i;
    scanf("%d%d",&n,&m);

        for(i=1;i<=n;i++)
            f[i]=i;
        memset(mark,0,sizeof(mark));
        int wrong=0;
        while(m--)
        {
            int d,a,b;
            scanf("%d%d%d",&d,&a,&b);
            if(a>n||b>n||a<1||b<1)
            {
                wrong++;
                //printf("Wrong\n");
                continue;
            }
            int x=finde(a);
            int y=finde(b);
            if(d==1)
            {
                if(x!=y)
                    same(a,b);
                else
                {
                    if(mark[a]!=mark[b])
                    {
                        //printf("Wrong\n");
                        wrong++;
                    }
                }
            }
            if(d==2)
            {
                if(x!=y)
                    make(a,b);
                else
                {
                    if(mark[a]+1!=mark[b]&&mark[a]!=mark[b]+2)
                    {
                        //printf("Wrong\n");
                        wrong++;
                    }
                }
            }
        }
        printf("%d\n",wrong);

    return 0;
}



你可能感兴趣的:(并查集)