//7968286    vrs    1182    Accepted    548K    219MS    C    1078B    2010-12-04 21:33:24
//1182 食物链 利用向量偏移的并查集
//
//每个集合都有三类动物,用rank表示,0——同类;1——食物;2——天敌,初始时都以自己为根节点,且rank为0
//只需对每一句话,判断x,y是否同一集合内,如果是就判断语句真假,否则就合并集合
//根据公式知道x-y=d-1,主要想出几个推导公式:
//1:  rank[x]=(rank[x]+rank[temp])%3   
//     这个最好设一两个例子验证一下,由于每次都是两集合的根合并,且只有根更改rank,所以每个x的rank值只会与
//     它的father有关(只有一次father更改,就是跟合并时),然后结合向量偏移得到。
//2:  rank[rooty]=(rank[x]-rank[y]-excusion+3)%3;
//3:  (rank[x]-rank[y]+3)%3!=d-1;
//三个公式都与向量偏移计算得来,看下面的详解
#include<stdio.h>

 

unsigned int setFather[50005];
unsigned int rank[50005];

unsigned int Find(unsigned int x)
{
    unsigned int temp;
    if(x==setFather[x])
        return x;
    temp=setFather[x];
    setFather[x]=Find(setFather[x]);
    //这里注意,在路径压缩过程中,由于递归时是先算根然后一直往叶子算过来,所以seFather[x]并不是原来x的父亲
    rank[x]=(rank[x]+rank[temp])%3;   //第一个
    return setFather[x];
}

void Union(unsigned int x,unsigned int y,int excusion)
{
    unsigned int rootx,rooty;
    rootx=Find(x);
    rooty=Find(y);
    setFather[rooty]=rootx;
    rank[rooty]=(rank[x]-rank[y]-excusion+3)%3;   //第二个
}

int main()
{
    unsigned int n,k,i;
    unsigned int d,x,y;
    unsigned int wrongNum;
    unsigned int rootx,rooty;
    scanf("%d%d",&n,&k);
    wrongNum=0;
    for(i=1;i<=n;i++)
    {
        setFather[i]=i;
        rank[i]=0;
    }
    while(k--)
    {
        scanf("%d%d%d",&d,&x,&y);
        if(x>n || y>n || (d==2 && x==y))
        {
            wrongNum++;
            continue;
        }
        rootx=Find(x);
        rooty=Find(y);
        if(rootx!=rooty)
            Union(x,y,d-1);
        else
        {
            if((rank[x]-rank[y]+3)%3!=d-1)   //第三个
                wrongNum++;
        }
    }
    printf("%d\n",wrongNum);
    return 0;
}

//附1:
//向量偏移解说
/*
> 什么叫做向量的思维模式?
> Orz Orz
我的理解是,对于集合里的任意两个元素a,b而言,它们之间必定存在着某种联系,

因为并查集中的元素均是有联系的,否则也不会被合并到当前集合中。那么我们

就把这2个元素之间的关系量转化为一个偏移量,以食物链的关系而言,不妨假设

a->b 偏移量0时 a和b同类

a->b 偏移量1时 a吃b

a->b 偏移量2时 a被b吃,也就是b吃a

有了这些基础,我们就可以在并查集中完成任意两个元素之间的关系转换了。

不妨继续假设,a的当前集合根节点aa,b的当前集合根节点bb,a->b的偏移值为d-1(题中给出的询问已知条件)

(1)如果aa和bb不相同,那么我们把bb合并到aa上,并且更新delta[bb]值(delta[i]表示i的当前集合根节点到i的偏移量)

此时 aa->bb = aa->a + a->b + b->bb,可能这一步就是所谓向量思维模式吧

上式进一步转化为:aa->bb = (delta[a]+d-1+3-delta[b])%3 = delta[bb],(模3是保证偏移量取值始终在[0,2]间)

(2)如果aa和bb相同,那么我们就验证a->b之间的偏移量是否与题中给出的d-1一致

此时 a->b = a->aa + aa->b = a->aa + bb->b,

上式进一步转化为:a->b = (3-delta[a]+delta[b])%3,

若一致则为真,否则为假。
*/


//附2:
/*
//这是自己原来想的方法,很麻烦。。。
//用了两重并查集,结果是300个测试数据过了,3000个就WA,郁闷得要死。。。
//我的思路是每个森林都有3类ABC,假如第一次碰到动物k,就新建森林容纳他们,一旦发现他们分别属于森林且有1,或2
//的关系,就合并森林,同时把三类分别合并一起。。。
#include<stdio.h>
#include<string.h>
#define bool int
#define NONESTATES 50004

typedef struct _RELATIVE
{
    unsigned int father;
    unsigned int next;
    unsigned int last;
    int typeABC;
}RELATIVE;
RELATIVE setRelative[50005];

unsigned int setFather[50005];
bool setState[50005];

//查找在哪个森林
unsigned int Find(unsigned int x)
{
    unsigned int temp,root;
    root=x;
    while(setFather[root]!=root)
        root=setFather[root];
    temp=x;
    while(setFather[x]!=x)
    {
        temp=setFather[x];
        setFather[x]=root;
        x=temp;
    }
    return root;
}

//查找在哪个类中
unsigned int FindType(unsigned int x)
{
    unsigned int temp,root;
    root=x;
    while(setRelative[root].father!=root)
        root=setRelative[root].father;
    temp=x;
    while(setRelative[x].father!=x)
    {
        temp=setRelative[x].father;
        setRelative[x].father=root;
        x=temp;
    }
    return root;
}

//合并两个森林中的三类
//这里的操作非常复杂,我自己都看晕了,跳过吧
void UnionType(unsigned int x,unsigned int y,int type)
{
    unsigned int subRootX,subRootY;
    subRootX=FindType(x);
    subRootY=FindType(y);
    if(type==1)
    {
        //处理A类关系
        setRelative[subRootY].father=subRootX;

        //处理B类关系
        if(setRelative[subRootY].next!=NONESTATES && setRelative[subRootX].next!=NONESTATES)
            setRelative[ setRelative[subRootY].next ].father=setRelative[subRootX].next;
        else if(setRelative[subRootY].next!=NONESTATES)
        {
            setRelative[ setRelative[subRootY].next ].typeABC=(setRelative[subRootX].typeABC+1)%3;
            setRelative[ setRelative[subRootY].next ].last=subRootX;
            setRelative[subRootX].next=setRelative[subRootY].next;
            if(setRelative[subRootX].last!=NONESTATES)
                setRelative[ setRelative[subRootY].next ].next=setRelative[subRootX].last;
        }

        //处理C类关系
        if(setRelative[subRootY].last!=NONESTATES && setRelative[subRootX].last!=NONESTATES)
            setRelative[ setRelative[subRootY].last ].father=setRelative[subRootX].last;
        else if(setRelative[subRootY].last!=NONESTATES)
        {
            setRelative[ setRelative[subRootY].last ].typeABC=(setRelative[subRootX].typeABC+2)%3;
            setRelative[ setRelative[subRootY].last ].next=subRootX;
            setRelative[subRootX].last=setRelative[subRootY].last;
            if(setRelative[subRootX].next!=NONESTATES)
                setRelative[ setRelative[subRootY].last ].last=setRelative[subRootX].next;
        }
    }
    else
    {
        //处理Y所在的A类关系
        if(setRelative[subRootX].next!=NONESTATES)
            setRelative[subRootY].father=setRelative[subRootX].next;
        else
        {
            setRelative[subRootX].next=subRootY;
            setRelative[subRootY].typeABC=(setRelative[subRootX].typeABC+1)%3;
            setRelative[subRootY].last=subRootX;
            if(setRelative[subRootX].last!=NONESTATES)
                setRelative[subRootY].next=setRelative[subRootX].last;
        }

        //处理Y所在的B类关系
        if(setRelative[subRootY].last!=NONESTATES)
            setRelative[ setRelative[subRootY].last ].father=subRootX;

        //处理Y所在的C类关系
        if(setRelative[subRootX].last!=NONESTATES && setRelative[subRootY].next!=NONESTATES)
            setRelative[ setRelative[subRootY].next ].father=setRelative[subRootX].last;
        else if(setRelative[subRootY].next!=-1)
        {
            setRelative[subRootX].last=setRelative[subRootY].next;
            setRelative[ setRelative[subRootY].next ].typeABC=(setRelative[subRootX].typeABC+2)%3;
            setRelative[ setRelative[subRootY].next ].next=subRootX;
            if(setRelative[subRootX].next!=NONESTATES)
                setRelative[ setRelative[subRootY].next ].last=setRelative[subRootX].next;
        }
    }
}

//合并两个森林
void Union(unsigned int x,unsigned int y,int type)
{
    unsigned int rootX,rootY,temp;
    rootX=Find(x);
    rootY=Find(y);
    if(rootX>rootY)
    {
        temp=rootX;
        rootX=rootY;
        rootY=temp;
    }
    setFather[rootY]=rootX;


    if(type==NONESTATES)
        return;

    UnionType(x,y,type);
}

//如果是存在第一次遇到的动物,就合并或新建一个森林
void NewSet(unsigned int x,unsigned int y,int statementType)
{
    unsigned int tempRoot;
    Union(x,y,NONESTATES);
    if(statementType==1)
    {
        if(setState[y]==1)
            setRelative[x].father=y;
        else if(setState[x]==1)
            setRelative[y].father=x;
        else
        {
            setRelative[y].father=x;
            setRelative[x].typeABC=0;
        }
    }
    else
    {
        if(setState[y]==1)
        {
            tempRoot=FindType(y);
            if(setRelative[tempRoot].last==NONESTATES)
            {
                setRelative[tempRoot].last=x;
                setRelative[  setRelative[tempRoot].next ].next=x;

                setRelative[x].typeABC=(setRelative[tempRoot].typeABC+2)%3;
                setRelative[x].next=tempRoot;
                setRelative[x].last=setRelative[tempRoot].next;
            }
            else
                setRelative[x].father=setRelative[tempRoot].last;
        }
        else if(setState[x]==1)
        {
            tempRoot=FindType(x);
            if(setRelative[tempRoot].next==NONESTATES)
            {
                setRelative[tempRoot].next=y;
                setRelative[ setRelative[tempRoot].last ].last=y;

                setRelative[y].typeABC=(setRelative[tempRoot].typeABC+1)%3;
                setRelative[y].next=setRelative[tempRoot].last;
                setRelative[y].last=tempRoot;
            }
            else
                setRelative[y].father=setRelative[tempRoot].next;
        }
        else
        {
            setRelative[x].typeABC=0;
            setRelative[x].next=y;
            setRelative[y].typeABC=1;
            setRelative[y].last=x;
        }
    }
}

//两个动物都出现过的,可以判断是真是假
bool Judge(unsigned int x,unsigned int y,int statementType)
{
    if(statementType==1 && setRelative[ FindType(x) ].typeABC==setRelative[ FindType(y) ].typeABC)
        return 1;
    else if(statementType==2 && (setRelative[ FindType(x) ].typeABC+1)%3==setRelative[ FindType(y) ].typeABC)
        return 1;
    return 0;
}

int main()
{
    unsigned int n,k;
    unsigned int D,X,Y;
    unsigned int i;
    long wrongStatementNum;

    scanf("%d %d",&n,&k);
    memset(setState,0,sizeof(setState));
    for(i=1;i<=n;i++)
    {
        setFather[i]=i;
        setRelative[i].father=i;
        setRelative[i].next=NONESTATES;
        setRelative[i].last=NONESTATES;
    }
    wrongStatementNum=0;
    while(k--)
    {
        scanf("%d %d %d",&D,&X,&Y);
        if((X>n || Y>n) || (D==2 && X==Y))
        {
            wrongStatementNum++;
            continue;
        }
        if(setState[X]==1 && setState[Y]==1)
        {
            if(Find(X)==Find(Y))
            {
                if(Judge(X,Y,D)==0)
                    wrongStatementNum++;
            }
            else
                Union(X,Y,D);
        }
        else
        {
            NewSet(X,Y,D);
        }
        setState[X]=setState[Y]=1;
    }
    printf("%ld\n",wrongStatementNum);
    return 0;
}
*/

posted on 2010-12-08 20:46  VRS  阅读(496)  评论(0编辑  收藏  举报