种类并查集+入门题A Bug's Life

我觉得种类并查集还是先从一个基础入门题讲起吧。

Background
Professor Hopper is researching the sexual behavior of a rare species of bugs. He assumes that they feature two different genders and that they only interact with bugs of the opposite gender. In his experiment, individual bugs and their interactions were easy to identify, because numbers were printed on their backs.
Problem
Given a list of bug interactions, decide whether the experiment supports his assumption of two genders with no homosexual bugs or if it contains some bug interactions that falsify it.

Input

The first line of the input contains the number of scenarios. Each scenario starts with one line giving the number of bugs (at least one, and up to 2000) and the number of interactions (up to 1000000) separated by a single space. In the following lines, each interaction is given in the form of two distinct bug numbers separated by a single space. Bugs are numbered consecutively starting from one.

Output

The output for every scenario is a line containing "Scenario #i:", where i is the number of the scenario starting at 1, followed by one line saying either "No suspicious bugs found!" if the experiment is consistent with his assumption about the bugs' sexual behavior, or "Suspicious bugs found!" if Professor Hopper's assumption is definitely wrong.

Sample Input

2
3 3
1 2
2 3
1 3
4 2
1 2
3 4

Sample Output

Scenario #1:
Suspicious bugs found!

Scenario #2:
No suspicious bugs found!

Hint

Huge input,scanf is recommended.

 

#include <algorithm>
#include <iostream>
#include <string>
#include <vector>
#include <cstring>
#include <cstdio>
#include <stack>
#include <set>
#define MAXN 2004
using namespace std;

int f[MAXN],r[MAXN];
bool flag;

int getf(int x){
    if(x == f[x]) return x;
    int t = getf(f[x]);
    r[x] = (r[x] + r[f[x]]) % 2;
    f[x] = t;
    return t;
}

void Union(int a,int b){
    int fa = getf(a);
    int fb = getf(b);
    if(fa == fb){
        if(r[a] == r[b]){
            flag = false;
        }
        return;
    }
    f[fb] = fa;
    r[fb] = (r[a]+1-r[b]+2)%2;
}

int main(){
    int t;
    scanf("%d",&t);
    for(int i=1;i<=t;++i){
        int n,m;
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;++i){
            f[i] = i;
            r[i] = 0;
        }
        flag = true;
        int a,b;
        for(int i=0;i<m;++i){
            scanf("%d%d",&a,&b);
            if(flag) Union(a,b);
        }
        printf("Scenario #%d:\n",i);
        if(flag) cout << "No suspicious bugs found!" << endl;
        else cout << "Suspicious bugs found!" << endl;
        cout << endl;
    }
    return 0;
}

其实乍一看,种类并查集和基础并查集大致一样,无非就是多了两句代码:getf函数中的 r[x] = (r[x] + r[f[x]]) % 2 和 Union函数中的 r[fb] = (r[a]+1-r[b]+2)%2,不用多想,这两句就是种类并查集的关键,所以在这里就重点解释这两句的意思。

首先,就这道题而言,很明显昆虫之间就两种关系,同性和异性,我们分别标记为0 和 1,用 r 数组存放,具体点说就是 r[i] == 0 表示 i 跟 f[i] 是同性,反之异性。

首先我们定义一种关系:f[a] -> a 代表 r[a] ,记准这个,在以后的推理中很有帮助。

在getf函数中,我们假设最终的根节点为root(定义为root只是为了帮助理解,这里不用多想,往下看就行了),然后在压缩路径的同时,首先看到关键代码:r[x] = (r[x] + r[f[x]]) % 2,这一句其实是实时更新 r[x], 因为我们知道,在压缩路径的同时,x的直接父节点f[x]可能就会变了(变的话就是变为root),所以r[x]也可能随之改变,怎么变呢?是不是可以这样表示:root->x = root->f[x] + f[x]->x。有可能你会问,root也不一定是f[x]的直接父节点啊,记住,因为路径压缩是递归的,所以在此之前,root已经是f[x]的直接父节点了,所以root->f[x] 这一句是没毛病的。根据我们之前的关系将这句翻译过来不就是r[x] = (r[x] + r[f[x]]) % 2,至于为什么%2,是因为在这个题中我们只定义了0,1两种关系,正确性可以自己验证。

在Union函数中涉及了两种情况:fa == fb,即a,b根节点相同,属于同一棵树,只判断a和b跟根节点的关系是否相同就行了,即r[a] == r[b]。

fa != fb时,分属两棵树,需要将它们联系在一起,做法就是将一个根节点挂在另一个根节点上,即f[fb] = fa,同时更新 r数组的关系,怎么更新?我们的做法是将fb挂在fa上,根据我们定义的关系,可以这样表示 fa->fb = fa->a + a->b + b->fb,翻译过来就是r[fb] = (r[a] + 1 - r[b] + 2) %2,其中 + 1是题目暂时给出的关系,为异性,+2是避免负数。

大致就是这么个意思吧。我觉得之前说的要记准的那个关系我觉得挺好用的,推理很方便,还有要记得活用,这个就废话了,还是要多多练习。

POJ 1703

POJ 1182 

  • 10
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值