Skip to content

11633: 【原1633】African

题目

题目描述

author: TA 原OJ链接:https://acm.sjtu.edu.cn/OnlineJudge-old/problem/1633

Description

黑助教由于外号不吉利的缘故导致脸一直都很黑,被助教组公认为非洲酋长。众所周知,非洲人是一种极易传染的体质,如果经常在一起谈笑风生的一群助教中有一个或以上的非洲人,那么这一群助教都会变成非洲人。某日方神欲氪一单648补全图鉴(剧情需要,请勿模仿),但又怕已经被小黑传染,成了非洲人,于是他求助于你。方神会告诉你助教组的总人数n和助教组中小团体的个数m(小团体中的助教会经常一起谈笑风生,一位助教可以在零个或多个多个小团体内),请你告诉方神助教组中已经有多少个非洲人了。假设一开始只有小黑一个非洲人。

Input Format

第一行两个整数n, m,表示助教组中有n位助教,m个小团体。接下来m行,每行开始一个数k,表示该小团体中共有k位助教经常一起谈笑风生,接下来k个0到n-1之间的整数,表示该小团体内的助教。注意黑助教永远是0号。

Output Format

一个整数,表示助教组中的非洲人的个数。

Sample Input

100 4 
2 1 2 
5 10 13 11 12 14 
2 0 1 
2 99 2

Sample Output

4

数据范围

  • 对于40%的数据,n <= 100, m <= 10;
  • 对于100%的数据,n <= 30000, m <= 500。

BugenZhao's solution

//
// Created by BugenZhao on 2019/5/19.
//

#include <iostream>
#include <string>

using std::ios, std::cin, std::cout, std::endl, std::string;
using ll = long long;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int n, m;
    cin >> n >> m;

    int *id = new int[n];
    int *sz = new int[n];
    int count = n;
    for (int i = 0; i < n; ++i) {
        id[i] = i, sz[i] = 1;
    }

    auto find = [&](int p) {
        while (p != id[p]) p = id[p];
        return p;
    };

    auto uni = [&](int p, int q) {
        int pr = find(p);
        int qr = find(q);
        if (pr == qr) return;
        if (sz[pr] < sz[qr]) {
            id[pr] = qr;
            sz[qr] += sz[pr];
        } else {
            id[qr] = pr;
            sz[pr] += sz[qr];
        }
        --count;
    };

    while (m--) {
        int k, root, tmp;
        cin >> k >> root;
        for (int i = 0; i < k - 1; ++i) {
            cin >> tmp;
            uni(tmp, root);
        }
    }

    int id0 = find(0);
    int ans = 0;
    for (int i = 0; i < n; ++i) {
        if (find(i) == id0) ++ans;
    }

    cout << ans << endl;
    delete[] id;
    delete[] sz;
    return 0;
}

ligongzzz's solution

#include <iostream>
#include <cstring>
using namespace std;

class dset {
public:
    int set_data[30009] = { 0 };
    dset() {
        memset(set_data, -1, sizeof(set_data));
    }
    void set_union(int root1, int root2) {
        if (root1 == root2)
            return;
        if (set_data[root1] < set_data[root2]) {
            set_data[root1] += set_data[root2];
            set_data[root2] = root1;
        }
        else {
            set_data[root2] += set_data[root1];
            set_data[root1] = root2;
        }
    }
    int set_find(int pos) {
        if (set_data[pos] < 0)
            return pos;
        return set_data[pos] = set_find(set_data[pos]);
    }
};

dset set_data;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);

    int n, m;
    cin >> n >> m;

    for (; m > 0; --m) {
        int k;
        cin >> k;
        int temp;
        cin >> temp;
        for (; k > 1; --k) {
            int i;
            cin >> i;
            set_data.set_union(set_data.set_find(temp), set_data.set_find(i));
        }
    }

    cout << -set_data.set_data[set_data.set_find(0)];

    return 0;
}

skyzh's solution

#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;

const int MAX_CONNECTION = 30001;
int link[MAX_CONNECTION];

int parent(int x) {
    if (link[x] == x) return x;
    return link[x] = parent(link[x]);
}

int make_link(int i, int j) {
    int a = parent(i), b = parent(j);
    link[a] = b;
}

int main() {
    int n, m;
    scanf("%d%d", &n, &m);
    for (int i = 0; i < n; i++) link[i] = i;
    for (int i = 0; i < m; i++) {
        int t;
        int op1, op2;
        scanf("%d%d", &t, &op1);
        for (int j = 1; j < t; j++) {
            scanf("%d", &op2);
            make_link(op1, op2);
        }
    }
    int cnt = 0;
    for (int i = 0; i < n; i++) {
        if (parent(0) == parent(i)) cnt++;
    }
    cout << cnt << endl;
    return 0;
}