POJ 1182 食物链(种类并查集)

  记得第一次做这道题的时候,推关系感觉有点复杂,而且写完代码后一直WA,始终找不出错误。 在A了十几道并查集后,再做这道题,发现太小儿科了。发现原来之所以WA,就在于查找根节点时,没有同步更新子节点相对根节点的关系。现在对并查集的感觉就在于,并查集的精髓就在于如何更新子节点与父节点的相对关系。

0:与根节点同类;1:被根节点吃;2:吃根节点

如何更新:

 设x的根节点为fx,y的根节点为fy。

1.若fx!=fy:

   合并fx、fy(将fy的父亲设为fx),那么要更新fy相对fx的关系。

  fy相对y的关系为:3-rel[y],y相对x的关系为d-1(d即为数据中的d),x相对fx的关系为rel[x]。

  那么fy相对fx的关系即为:(3-rel[y]+d-1+rel[x])%3。

2.若fx==fy:

  那么则要判断由此推算出的y相对x的关系,是否等于数据给出的d-1。若不相等,则是假话。

   y相对fy/fx的关系为rel[y],fy/fx相对x的关系为3-rel[x],

  则y相对x的关系为:(rel[y]+3-rel[x])%3。

至于其他判断它为假话的条件就很好办了。

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>

using namespace std;
const int maxn=50010;
int father[maxn];
int rel[maxn];//rel[i]表示i相对根节点的关系,0:与根节点同类;1:被根节点吃;2:吃根节点
int n,k;
void init(){
    for(int i=0;i<maxn;i++){
        father[i]=i;
        rel[i]=0;
    }
}

int find_root(int x){
    if(father[x]==x)
        return x;
    int tmp=father[x];
    father[x]=find_root(father[x]);
    rel[x]=(rel[x]+rel[tmp])%3;
    return father[x];
}
void Union(int x,int y,int fx,int fy,int d){
    father[fy]=fx;
    rel[fy]=(3-rel[y]+d+rel[x])%3;
}
int main()
{
    int ans=0,d,x,y; //ans统计假话的个数
    scanf("%d%d",&n,&k);
    init();//又忘记初始化了啊啊啊
    for(int i=1;i<=k;i++){
        scanf("%d%d%d",&d,&x,&y);
        //若有大于n,则为假话
        if(x>n||y>n){
            ans++;
            continue;
        }

        if(d==1){
            d=0;
        }
        else{
            d=1;
            //若x吃x,则是假的
            if(x==y){
                ans++;
                continue;
            }
        }
        int fx=find_root(x);
        int fy=find_root(y);
        if(fx==fy){
            int t=(rel[y]+3-rel[x])%3;
            if(t!=d){
                ans++;
            }
        }
        else{
            Union(x,y,fx,fy,d);
        }
    }
    printf("%d
",ans);
    return 0;
}
原文地址:https://www.cnblogs.com/chenxiwenruo/p/3319369.html