种类并查集学习

种类并查集

学习网站:https://zhuanlan.zhihu.com/p/97813717

种类并查集(包括普通并查集)维护的是一种循环对称的关系。

img

A Bug's Life

题目大意:研究表明虫子都是异性相吸,给你 \(n\) 对虫子,\(a\)\(b\) 表示这两只虫子相互吸引。问是否可以找到两只虫子是同行相吸,如果可以,请输出“Suspicious bugs found!”,否则请输出“No suspicious bugs found!”

题解:

这个题目很简单,可以把所有相同性别的人放到一个圈里,然后判断 \(a\)\(b\) 是不是在一个圈里即可。

我们开一个两倍大小的并查集。例如,假如我们要维护4个元素的并查集,我们改为开8个单位的空间:

img

\(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=%d\n",#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");
    }
}

P2024 [NOI2001]食物链

我们会发现,A、B、C三个种群天然地符合用种类并查集维护的要求。

img

于是我们可以用一个三倍大小的并查集进行维护,用 \(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=%d\n",#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("%d\n",ans);
    return 0;
}

P1525 关押罪犯

这里也有满足种类并查集的关系

img

我们开一个两倍大小的并查集。例如,假如我们要维护4个元素的并查集,我们改为开8个单位的空间:

img

对于罪犯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=%d\n",#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("%d\n",ans);
   return 0;
}
posted @ 2020-07-09 22:15  EchoZQN  阅读(118)  评论(0编辑  收藏  举报