poj1182食物链(种类并查集)

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

并查集维护的是一种相对关系;

而要解决敌人的敌人是朋友(也就是循环对称)的这种关系,用种类并查集来解决

如果是三个及以上的集合,只要每个集合都是等价的,且集合间的每个关系都是等价的,就能够用种类并查集进行维护。

题意:

 

 观察下,(普通并查集做法)发现三个集合可以用种类并查集来解决:可以用一个三倍大小的并查集进行维护,用i+n表示i的捕食对象,而i+2n表示i的天敌。

(带权并查集做法)带权并查集的权值表示两个动物在食物链上的相对关系A->B=0(同类),A->B=1(A吃B),A->B=2(B吃A);

注意三个点:

1,路径压缩时,如何更新value值:

A->B为1,B->C为1,怎么求A->C?显然A吃B,B吃C,那么C应该吃A,A->C应该为2;

A->B为2,B->C为2,怎么求A->C?显然B吃A,C吃B,那么A应该吃C,A->C应该为1;

A->B为0,B->C为1,怎么求A->C?显然A、B同类,那么A应该吃C,A->C应该为1;

观察可发现规律:A->C = (A->B + B->C) % 3;

2,区间合并时,如何更新value值:

普通的带权合并上加个取模即可,va[fx]=(s+va[y]-va[x])%3;

3,如何判断是否矛盾

为了防止负数的影响 A->B=(A->C - B->C + 3) % 3,将A->B与题中给定的值判断即可

因为题中只有1和2的情况,所以--d;

代码:

#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
#define maxx 1010
#define maxn 50010
int fa[maxn],va[maxn];
int Find(int x)
{
    if(x!=fa[x])
    {
        int t=fa[x];
        fa[x]=Find(fa[x]);
        va[x]=(va[x]+va[t])%3;//合并权值
    }
    return fa[x];
}
void Union(int x,int y,int s)
{
    int fx=Find(x);
    int fy=Find(y);
    if(fx!=fy)
    {
        fa[fx]=fy;
        va[fx]=s+va[y]-va[x];
    }
}
int main()
{
    for(int i=0; i<=maxn-10; i++)
         fa[i]=i;
    int n,k;
    cin>>n>>k;
    int ans=0;
    while(k--)
    {
        int d,x,y;
        cin>>d>>x>>y;
        if((x==y&&d==2)||x>n||y>n)
            ans++;
        else
        {
            int fx=Find(x),fy=Find(y);
            if(fx==fy)
            {
                if((d-1)!=(va[x]-va[y]+3)%3)
                    ans++;
            }
            else
            {
                fa[fx]=fy;
                va[fx]=((d-1)+va[y]-va[x])%3;
            }
        }
    }
    cout<<ans<<endl;
    return 0;}

猜你喜欢

转载自www.cnblogs.com/sweetlittlebaby/p/12654817.html