[POJ]并查集三连发

整理一下最近的题解,这是三个并查集的题目,分别是:

POJ 1182 食物链
POJ 1611 The Suspects
POJ 2524 Ubiquitous Religions

POJ 1182 食物链

这个题有一个很棒的套路,那就是用一个并查集来维护三个集合内的元素,可以使这个并查集扩大n倍,然后按照元素是属于哪一个集合来进行运算,放入并查集对应范围内的对应位置。代码如下:

#include <iostream>
#include <cstdio>
#include <cstring>

using namespace std;

const int maxn = 0x7ffffff;
int pre[maxn];

int find(int x) {
    return x == pre[x] ? x : pre[x] = find(pre[x]);
}

void unite(int x, int y) {
    x = find(x);
    y = find(y);
    if(x != y) {
        pre[y] = x;
    }
}

int main() {
    int N, K, D, x, y;
    scanf("%d %d", &N, &K);
    int mm = N * 3;
    for(int i = 0; i < mm; i++) {
        pre[i] = i;
    }
    int ans = 0;

    while(K--) {
        scanf("%d %d %d", &D, &x, &y);
        if(x > N || y > N) {
            ans++;
            continue;
        }
        if(D == 1) {    //同类
            if(find(x) == find(y+N) || find(x) == find(y+2*N)) {
                ans++;  //矛盾
            }
            else {
                unite(x, y);        //放入A
                unite(x+N, y+N);    //放入B
                unite(x+2*N, y+2*N);//放入C
            }
        }
        else if(D == 2) {   //不同类
            if(find(x) == find(y) || find(x) == find(y+2*N)) {
                ans++;  //矛盾
            }
            else {
                unite(x, y+N);      //x放入A,y放入B
                unite(x+N, y+2*N);  //x放入B,y放入C
                unite(x+2*N, y);    //x放入B,y放入A
            }
        }
    }
    printf("%d\n", ans);
}

POJ 1611 The Suspects

这道题可以在输入的时候进行处理,单独读入一个元素后,再循环读入剩下的,再与每一个元素的前者进行合并操作(如果不是同一个father)。在并查集的unite操作中来动态统计合并到某两个元素的时候此时的suspects的数量,这样的话,只需要找到0号的father就可以找到suspects的总人数了(默认0是患病的)。代码如下:

 1 #define _CRT_SECURE_NO_WARNINGS
 2 #include <cstdlib>
 3 #include <cstdio>
 4 #include <cstring>
 5 #include <iostream>
 6
 7 using namespace std;
 8
 9 const int maxn = 30010;
10 int father[maxn];
11 int num[maxn];
12 int n, m;
13
14 void init() {
15     for (int i = 0; i < maxn; i++) {
16         father[i] = i;
17         num[i] = 1;
18     }
19 }
20 int find(int x) {
21     return x == father[x] ? x : father[x] = find(father[x]);
22 }
23 void unite(int x, int y) {
24     x = find(x);
25     y = find(y);
26     if(x != y) {
27         father[x] = y;
28         num[y] += num[x];
29     }
30 }
31
32 int main(int argc, char *argv[]) {
33     while (~scanf("%d %d", &n, &m) && n + m) {
34         init();
35         int group;
36         int tmp[maxn];
37         int ans = 0;
38         for (int ii = 0; ii < m; ii++) {
39             memset(tmp, 0, sizeof(tmp));
40             scanf("%d", &group);
41             scanf("%d", &tmp[0]);
42             for (int i = 1; i < group; i++) {
43                 scanf("%d", &tmp[i]);
44                 if (father[tmp[i]] != father[tmp[i - 1]]) {
45                     unite(tmp[i], tmp[i - 1]);
46                 }
47             }
48         }
49         // for (int i = 0; i < n; i++) {
50         //     if (father[i] == 0) {
51         //         ans++;
52         //     }
53         // }
54         printf("%d\n", num[find(0)]);
55     }
56     return 0;
57 }

POJ 2524 Ubiquitous Religions

额外加一个vis来判断某信仰是否被遍历到,利用并查集“合并过的两个元素其中一个元素的father必定不是本身”这一性质统计一共有多少个集合。代码如下:

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <cstring>
 4 #include <algorithm>
 5 using namespace std;
 6
 7 const int maxn = 50010;
 8 int pre[maxn];
 9 int vis[maxn];
10 int n, m;
11
12 int find(int x) {
13     return x == pre[x] ? x : pre[x] = find(pre[x]);
14 }
15
16 void unite(int x, int y) {
17     x = find(x);
18     y = find(y);
19     if(x != y) {
20         pre[y] = x;
21     }
22 }
23 inline void init() {
24     for(int i = 0; i < maxn; i++) {
25         pre[i] = i;
26     }
27 }
28
29 int main() {
30     int kase = 1;
31     while(~scanf("%d %d", &n, &m) && n+m) {
32         init();
33         memset(vis, 0, sizeof(vis));
34         int a, b;
35         int cnt = 0;
36         for(int i = 0; i < m; i++) {
37             scanf("%d %d", &a, &b);
38             vis[a] = 1;
39             vis[b] = 1;
40             unite(a, b);
41         }
42         int k = 0;
43         // for(int i = 1; i <= n; i++) {
44         //  cout << pre[i] << " ";
45         // }
46         // cout << endl;
47         for(int i = 1; i <= n; i++) {
48             if(vis[i] && pre[i] == i) {
49                 cnt++;
50             }
51             if(!vis[i]) {
52                 cnt++;
53             }
54         }
55         printf("Case %d: %d\n", kase++, cnt);
56     }
57     return 0;
58 }

时间: 2024-08-10 15:50:03

[POJ]并查集三连发的相关文章

poj 并查集

http://poj.org/problem?id=1611 水题 题意:就是找一共有多少个人感染了,0是感染学生的编号. #include <stdio.h> #include <string.h> #define maxn 30005 int m,n; int belg[ maxn ]; int Find(int x) { int _x=x,_b; while( _x != belg[ _x ] ) _x = belg[ _x ]; while( x != belg[ x ]

POJ 2524 Ubiquitous Religions (幷查集)

Ubiquitous Religions Time Limit: 5000MS   Memory Limit: 65536K Total Submissions: 23090   Accepted: 11378 Description There are so many different religions in the world today that it is difficult to keep track of them all. You are interested in findi

poj 2492 a bug&#39;s life 简单种类并查集

题意大致为找同性恋的虫子.... 这个比食物链要简单些.思路完全一致,利用取余操作实现关系之间的递推. 个人感觉利用向量,模和投影可能可以实现具有更加复杂关系的并查集. 1 #include<cstdio> 2 using namespace std; 3 const int MAXN=50010; 4 int fa[MAXN]; 5 int rel[MAXN]; // 0代表同类,1代表吃fa[i],2代表被吃 6 void _set(int n) 7 { 8 for(int i=1;i&l

poj 2513 并查集,Trie(字典树), 欧拉路径

- Colored Sticks POJ - 2513 You are given a bunch of wooden sticks. Each endpoint of each stick is colored with some color. Is it possible to align the sticks in a straight line such that the colors of the endpoints that touch are of the same color?

[POJ 1988] Cube Stacking (带值的并查集)

题目链接:http://poj.org/problem?id=1988 题目大意:给你N个方块,编号从1到N,有两种操作,第一种是M(x,y),意思是将x所在的堆放到y所在的堆上面. 第二种是C(x),意思是数x方块下面有多少个方块. 把两堆合成一堆,这个可以用并查集来实现,问题是,怎么样维护x方块下面有多少个方块呢? 先来分析一下题目,按照样例,我们有6个方块,1,2,3,4,5,6. 令Cnt(x) = C(x)+1. 先执行M(1,6),此时Cnt(1) = 2, Cnt(6) = 1 再

HDU 1325 POJ 1308 Is It A Tree? (并查集)

这道题就是裸并查集,关键在于对不是树几种的判断 1. 空树是树 2. 森林不是树 3. 无环 或者从入度来看:1,无环:2,除了根,所有的入度为1,根入度为0:3,这个结构只有一个根,不然是森林了. 这道题本来暑假做的POJ 1308 但是HDU没有过.在于空树没有考虑. 用并查集判断有多少个森林注意编号是随机的,不是次序.... /* input: 0 0 1 1 0 0 1 2 1 2 0 0 1 2 2 3 4 5 0 0 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9

POJ 2492 (简单并查集) A Bug&#39;s Life

题意:有编号为1~n的虫子,开始假设这种昆虫是异性恋.然后已知xi 和 yi进行交配,根据已知情况分析能否推理出其中是否有同性恋 这道题和 POJ 1182 食物链 十分相似,不过在更新与父节点关系的时候要简单一些 sex数组保存的是与父节点的性别关系,如果与父节点是同性,则为0,否则是1 每次路径压缩的同时要更新sex[a] = (sex[a] + sex[temp]) % 2; 还有就是如果x 和 y 不在一个集合,两棵树进行合并的时候,考虑x px y py 四者之间的关系,有 paren

POJ 1984 Navigation Nightmare 二维带权并查集

题目来源:POJ 1984 Navigation Nightmare 题意:给你一颗树 k次询问 求2点之间的曼哈顿距离 并且要在只有开始k条边的情况下 思路:按照方向 我是以左上角为根 左上角为原点 dx[i]为i点距离根的x坐标 dy[]是y坐标 这两个可以通过路径压缩求出 只不过是二维而已 #include <cstdio> #include <cstdlib> #include <cstring> using namespace std; const int m

[ACM] POJ 3295 Ubiquitous Religions (并查集)

Ubiquitous Religions Time Limit: 5000MS   Memory Limit: 65536K Total Submissions: 23093   Accepted: 11379 Description There are so many different religions in the world today that it is difficult to keep track of them all. You are interested in findi