我来补博客例题了……
对于本题,我们维护不同物种之间的关系。设有动物A,B,则必有:
1.A>B
2.A=b
3.A<B.
所以,对于三种关系,我们都需要维护。
不妨,我们开三倍大小的并查集,其中,1~n是A种群,n+1~2n是B种群,2n+1~3n是C种群,并设有:
A>B,B>C,C>A.
每一种动物,在A,B,C种群中都有。设它们的目的,后面再说。
首先,对于A,B,如果它们是同一物种,那么,我们就需要把三个种群中的A和B全部merge.以此表示它们和平。
那么,对于A>B(即A吃B)呢?
从前文知,我们有3个群体,它们之间有捕食与被捕食的关系。那么,我们让A群中的A与B群中的Bmerge,B群中的A与C群中的Bmerge,C群中的A与A群中的Bmerge.
由于集群中互斥,以此维护其关系。
那么,考虑如何判断假话。
首先,对于A=B,则必有A与B互相不大于。即,A不吃B,B不吃A。那么,我们只要判断,A的当前集群的A与B的下一个集群的B是否在同一集合,或是B集群的B与A的下一个集群的A是否在同一集群内。如果符合上述条件,即假话。
否则,三个集合中的A与B全部合并。
那么,对于A>B呢(B>A)
首先,它们不能是同类,判断它们本身是否在一个集合即可。
其次,它们不能互相克制。即,A>B,B不能>A.
并查集显而易见了。
代码:
#include<cstdio> #include<iostream> #include<cstring> using namespace std; inline int read(){ char ch=getchar();int s=0; while(ch<'0'||ch>'9')ch=getchar(); while(ch>='0'&&ch<='9'){ s=(s<<1)+(s<<3)+(ch^48); ch=getchar(); }return s; } const int MAXN=100010; int n,m,ans,f[MAXN+(MAXN<<1)]; int find(int x){return x==f[x]?x:f[x]=find(f[x]);} int main(){ //freopen("P2024.in","r",stdin); //freopen("P2024.out","w",stdout); n=read(),m=read(); for(int i=1;i<=(n+(n<<1));++i)f[i]=i; for(;m;m--){ int opt=read(),u=read(),v=read(); if(u>n||v>n){ans++;continue;} if(opt==1){ if(find(u+n)==find(v)||find(u)==find(v+n))ans++; else{ f[find(u)]=find(v); f[find(u+n)]=find(v+n); f[find(u+(n<<1))]=find(v+(n<<1)); } }else{ if(find(u)==find(v)||find(u)==find(v+n))ans++; else{ f[find(u+n)]=find(v); f[find(u+(n<<1))]=find(v+n); f[find(u)]=find(v+(n<<1)); } } } printf("%d ",ans); //fclose(stdin); //fclose(stdout); return 0; }
其实,这道并查集与前面的并查集是有区别的,一者维护路径信息,一者维护多种关系。