最近玩的太嗨了,都忘了自己还有三篇博客还在拖更,也是今天一更到底好吧,边更新边写题,让看官老爷有更多的样题去联系
引入——
在学这个之前,我相信各位应该已经接触过了并查集了吧,嗯?什么?没有看过怎么办,那还不赶紧看,跳转链接——http://t.csdnimg.cn/Y880W
好了,那么现在应该都是学过的人了吧,OK,先来讲解种类并查集与并查集的不同的地方,众所周知,我们的并查集只能判断一种关系,是否和我在同一个集合,是或者不是,但是我们的种类并查集可以判断多种关系,比如说“朋友的朋友不一定是朋友,敌人的敌人有可能是朋友”这种关系,我们有多种关系,那么就轮到我们的种类并查集出马了
我们只需要开n*种类关系个大小 的数组空间即可,1~n表示第一种关系,n+1~2*n表示第二种关系,以此类推
一般的并查集,维护的是具有连通性、传递性的关系,例如亲戚的亲戚是亲戚。但是,有时候,我们要维护另一种关系:敌人的敌人是朋友。种类并查集就是为了解决这个问题而诞生的。
好了,这个说我,我们该进入实战环节了
例题
240. 食物链
思路:
如果x的同类属于某个集合find(x)内,
那么吃x的,即x的天敌,应该属于集合find(x+n)内,
那么x吃的,即x的猎物,应该属于集合find(x+2*n)内
判断1:如果x和y是同类
那么find(x)应该等于find(y),
x,y关系之前如果已经确定,需要判断是否下面情况,则说明说谎
1.find(x)==find(y+n)
2.find(x)==find(y+2*n)
x,y关系之前如果没有确定
p[find(x)]=find(y)
p[find(x+n)]=find(y+n)
p[find(x+2*n)]=find(y+2*n)
判断2:如果x吃y,x是y的天敌
那么find(x)应该等于find(y+n)
x,y关系之前如果已经确定,需要判断是否下面情况,则说明说谎
1.如果x=y-->x自己吃自己
2.find(x)==find(y)-->x和y同类
3.find(x)==find(y+2*n)-->y是x的天敌
x,y关系之前如果没有确定
p[find(x)]=find(y+n)
p[find(x+n)]=find(y+2*n)->x的天敌是y的猎物
p[find(x+2*n)]=find(y)->x的猎物是y的同类
#include<bits/stdc++.h>
using namespace std;
#define int long long int n,k;
int cnt;
int d,x,y;
int f[200005];int cha(int x)
{if(x==f[x])return x;return f[x]=cha(f[x]);
}signed main()
{cin>>n>>k;for(int i=1;i<=3*n;i++){f[i]=i;}for(int i=1;i<=k;i++){cin>>d>>x>>y;if(x>n||y>n){cnt++;continue;}else if(d==1)//如果是同类 {if(cha(x)==cha(y+n)){cnt++;continue;}if(cha(x)==cha(y+2*n)){cnt++;continue;}f[cha(x)]=cha(y);f[cha(x+n)]=cha(y+n);f[cha(x+2*n)]=cha(y+2*n);}else if(d==2)//如果是天敌{if(cha(x)==cha(y)){cnt++;continue;} if(cha(x)==cha(y+2*n)){cnt++;continue;}f[cha(x)]=cha(y+n);f[cha(x+n)]=cha(y+2*n);f[cha(x+2*n)]=cha(y);} }cout<<cnt;return 0;
}
P5937 [CEOI1999] Parity Game
用 𝑓[𝑖] 表示与自己奇偶性相同的集合,用]f[i+n] 表示与 𝑖i 奇偶性不同的元素集合。
用区间关系来确定奇偶性是否相同
#include<bits/stdc++.h>
using namespace std;
#define int long longint n,m;
struct node{int l;int r;int flag;
}a[5005];
int f[20005];
int b[20005];
string s;
int cha(int x)
{if(f[x]==x){return x;}return f[x]=cha(f[x]);
}void bing(int a,int b)
{f[cha(a)]=cha(b);return ;
}signed main()
{cin>>n>>m;for(int i=1;i<=m;i++){cin>>a[i].l>>a[i].r>>s;a[i].l--;if(s[0]=='o'){a[i].flag=1;}else{a[i].flag=2;}b[i]=a[i].l;b[i+1]=a[i].r;}sort(b+1,b+1+2*n);int len=(b+1,b+1+2*n)-(b+1);for(int i=1;i<=len*2;i++)//开双倍空间,有两种状态 {f[i]=i;}for(int i=1;i<=m;i++){a[i].l=lower_bound(b+1,b+1+len,a[i].l)-b;a[i].r=lower_bound(b+1,b+1+len,a[i].r)-b;if(a[i].flag==2)//区间奇偶性为偶数 {if(cha(a[i].l)==cha(a[i].r+len))//奇偶性不同,则是矛盾的 {cout<<i-1;return 0;}else{bing(a[i].l,a[i].r);bing(a[i].l+len,a[i].r+len);}}else{if(cha(a[i].l)==cha(a[i].r))//奇偶性不同,则是矛盾的 {cout<<i-1;return 0;}else{bing(a[i].l,a[i].r+len);bing(a[i].l+len,a[i].r);}}}cout<<m;return 0;
}
P1525 [NOIP2010 提高组] 关押罪犯
思路:先按照贪心对事件影响力从大到小进行排序,然后f[ i ]表示在一个监狱,f [ i +n]表示不在一个监狱,我们想要影响力最小,那肯定就要把排在前面的争取拉到不同的监狱,直到同处同一个监狱则直接输出当前事件影响力即可,如果一直没有发生冲突则输出0
#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,m;
struct node{int a,b,c;
}q[100005];
int f[40005];
bool cmp(node x,node y)
{return x.c>y.c;
}
int cha(int x)
{if(f[x]==x)return x;return f[x]=cha(f[x]);
}
void bing(int a,int b)
{f[cha(a)]=cha(b);return ;
}
signed main()
{cin>>n>>m;for(int i=1;i<=m;i++){cin>>q[i].a>>q[i].b>>q[i].c;}sort(q+1,q+1+m,cmp);for(int i=1;i<=2*n;i++){f[i]=i;}for(int i=1;i<=m;i++){if(cha(q[i].a)==cha(q[i].b)||cha(q[i].a+n)==cha(q[i].b+n)){cout<<q[i].c;return 0;}bing(q[i].a+n,q[i].b);bing(q[i].a,q[i].b+n);}cout<<0;return 0;
}