种类并查集
学习网站:https://zhuanlan.zhihu.com/p/97813717
种类并查集(包括普通并查集)维护的是一种循环对称的关系。
题目大意:研究表明虫子都是异性相吸,给你 (n) 对虫子,(a) 、(b) 表示这两只虫子相互吸引。问是否可以找到两只虫子是同行相吸,如果可以,请输出“Suspicious bugs found!”,否则请输出“No suspicious bugs found!”
题解:
这个题目很简单,可以把所有相同性别的人放到一个圈里,然后判断 (a) 、(b) 是不是在一个圈里即可。
我们开一个两倍大小的并查集。例如,假如我们要维护4个元素的并查集,我们改为开8个单位的空间:
(i+n) 表示与 (i) 的性别不同。
那么如果 1 和 2 的性别不同,那么 (merge(1,n+2)) (merge(n+1,2))
这样如果可以通过式子得到同行相吸则一定是在一个圈里。
#include <cstdio>
#include <cstdlib>
#define inf 0x3f3f3f3f
#define inf64 0x3f3f3f3f3f3f3f3f
#define debug(x) printf("debug:%s=%dn",#x,x);
//#define debug(x) cout << #x << ": " << x << endl
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int maxn = 2e6+10;
int f[maxn],a[maxn],b[maxn];
void init(int n){
for(int i=0;i<=2*n;i++) f[i]=i;
}
int findx(int x){
return f[x]==x?x:f[x]=findx(f[x]);
}
void merge(int x,int y){
x=findx(x);
y=findx(y);
if(x==y) return ;
f[x]=y;
}
bool same(int x,int y){
return findx(x)==findx(y);
}
int main(){
int t;
scanf("%d",&t);
for(int cas=1;cas<=t;cas++){
int n,m,ans=0;
scanf("%d%d",&n,&m);
init(2*n);
for(int i=1;i<=m;i++){
scanf("%d%d",&a[i],&b[i]);
if(same(a[i],b[i])) ans = 1;
merge(a[i],b[i]+n);
merge(b[i],a[i]+n);
}
printf("Scenario #%d:n",cas);
if(ans) printf("Suspicious bugs found!n");
else printf("No suspicious bugs found!n");
printf("n");
}
}
我们会发现,A、B、C三个种群天然地符合用种类并查集维护的要求。
于是我们可以用一个三倍大小的并查集进行维护,用 (i+n) 表示 (i) 的捕食对象,而 (i+2n) 表示 (i) 的天敌。
-
对于 (a) 、(b) 如果是同类关系,则 (a+n) 、(b+n) 也是同类, (a+2*n) 、(b+2*n) 也是同类
-
如果 (a) 、(b) 如果是捕食关系,则 (a) 、 (b+n) 也是捕食关系,(a+n)、(b+2*n) 也是捕食关系
所以只要判断有没有矛盾即可。
#include <cstdio>
#include <cstdlib>
#define inf 0x3f3f3f3f
#define inf64 0x3f3f3f3f3f3f3f3f
#define debug(x) printf("debug:%s=%dn",#x,x);
//#define debug(x) cout << #x << ": " << x << endl
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int maxn = 5e5+10;
int f[maxn];
void init(int n){
for(int i=0;i<=n;i++) f[i]=i;
}
int findx(int x){
return f[x]==x?x:f[x]=findx(f[x]);
}
void merge(int x,int y){
x=findx(x);
y=findx(y);
if(x==y) return ;
f[x]=y;
}
bool same(int x,int y){
return findx(x)==findx(y);
}
int main(){
int n,k,ans=0;
scanf("%d%d",&n,&k);
init(3*n);
for(int i=1;i<=k;i++){
int opt,x,y;
scanf("%d%d%d",&opt,&x,&y);
if(x<=0||y<=0||x>n||y>n||(opt==2&&x==y)) {
ans++;
continue;
}
if(opt==1){
if(same(x,y+n)||same(x,y+2*n)) ans++;
else merge(x,y),merge(x+n,y+n),merge(x+2*n,y+2*n);
}
else{
if(same(x,y)||same(x,y+n)) ans++;
else merge(x,y+2*n),merge(x+n,y),merge(x+2*n,y+n);
}
}
printf("%dn",ans);
return 0;
}
这里也有满足种类并查集的关系
我们开一个两倍大小的并查集。例如,假如我们要维护4个元素的并查集,我们改为开8个单位的空间:
对于罪犯i,i+n为他的敌人
所以如果 1 和 2 是敌人,那么merge(1,n+2),merge(n+1,2)
表示1要连向2的敌人,1和2的敌人是好朋友
2要连向1的敌人,2和1的敌人是好朋友
只要最后不连成一个圈则表示可以进行合理分配使得没有冲突事件的影响力
如果连成一个圈,则表示前面分配完不在一个监狱之后当前的a,b一定会处于同一个监狱
因为要冲突事件影响力越小越好,所以贪心的考虑先排影响力大的罪犯
#include <bits/stdc++.h>
#define inf 0x3f3f3f3f
#define inf64 0x3f3f3f3f3f3f3f3f
#define debug(x) printf("debug:%s=%dn",#x,x);
//#define debug(x) cout << #x << ": " << x << endl
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int maxn = 5e5+10;
int f[maxn];
struct node{
int a,b,c;
node(int a=0,int b=0,int c=0):a(a),b(b),c(c){}
}e[maxn];
bool cmp(node a,node b){
return a.c>b.c;
}
void init(){
for(int i=0;i<=maxn;i++) f[i]=i;
}
int findx(int x){
return f[x]==x?x:f[x]=findx(f[x]);
}
void merge(int x,int y){
x=findx(x);
y=findx(y);
if(x==y) return ;
f[x]=y;
}
bool same(int x,int y){
return findx(x)==findx(y);
}
int main(){
int n,m,ans=0;
scanf("%d%d",&n,&m);
init();
for(int i=1;i<=m;i++){
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
e[i]=node(a,b,c);
}
sort(e+1,e+1+m,cmp);
for(int i=1;i<=m;i++){
if(same(e[i].a,e[i].b)) {
ans = e[i].c;
break;
}
merge(e[i].a,e[i].b+n);
merge(e[i].b,e[i].a+n);
}
printf("%dn",ans);
return 0;
}
原文链接: https://www.cnblogs.com/EchoZQN/p/13276559.html
欢迎关注
微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍;
也有高质量的技术群,里面有嵌入式、搜广推等BAT大佬
原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/364262
非原创文章文中已经注明原地址,如有侵权,联系删除
关注公众号【高性能架构探索】,第一时间获取最新文章
转载文章受原作者版权保护。转载请注明原作者出处!