链接: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;}