POJ1182-并查集

这题有一个关键点: x的食物的食物以x为食,即生物间的关系是以3为循环的,就像运算 (0+1)%3=1,(1+1)%3=2,(2+1)%3=0,(0+1)%3=1... ...

不管d=1还是d=2,都表示x与y有关系,因此可以并到一个并查集里去,然而具体的同类与捕食关系可用0,1,2来代表;

这里以r[i]表示i与其并查集中父节点p[i]的关系: 0表示同类,1表示i吃p[i],2表是i被p[i]吃;

那么如果x和y在同一个并查集时,可通过查询x与根节点的关系,y与根节点的关系来判断x与y的关系。若在并查集的find操作中压缩路径的话,根节点也就成了父节点,好方便!

另外合并和查找时可以利用一些加法,对3取模的运算来更新r。

AC代码:

#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;

const int NN=50005;

int n;
int p[NN]; //根节点
int r[NN]; //与父节点的关系,0:同类,1:吃根节点,2:被根节点吃

inline void get(int &x)
{
    char c=getchar();
    while (c<'0' || c>'9') c=getchar();
    x=c-'0';
    c=getchar();
    while (c>='0' && c<='9') x=x*10+c-'0',c=getchar();
}

int find(int x)
{
    if (p[x]!=x)
    {
        int t=p[x];
        p[x]=find(p[x]);
        r[x]=(r[x]+r[t])%3; //x与新父节点(根节点)的关系
    }
    return p[x];
}

int Union(int d,int x,int y)
{
    int fx=find(x);
    int fy=find(y);
    p[fx]=fy;
    r[fx]=(r[y]-r[x]+2+d)%3; //fx与fy的关系=y与fy的关系和x与fx的关系差+x与y的关系
}

int main()
{
    int d,x,y,k,cnt=0;
    scanf("%d%d",&n,&k);
    for (int i=1; i<=n; i++)
    {
        p[i]=i;
        r[i]=0;
    }
    while (k--)
    {
        get(d); get(x); get(y);
        if (x>n || y>n) cnt++;
        else if (d==2 && x==y) cnt++;
        else if (find(x)!=find(y)) Union(d,x,y);
        else if ((r[y]+d+2)%3!=r[x]) cnt++;//这句话一定是在find(x)和find(y)之后的,因为find过后r[x]才是x与其根节点的关系
    }
    printf("%d\n",cnt);
    return 0;
}


你可能感兴趣的:(c,生物)