P3452 [POI2007]BIU-Offices(并查集+bfs)

P3452 [POI2007]BIU-Offices(并查集+bfs)

题目描述

Bytel is a mobile telephony potentate. Each employee has been issued a company phone, the memory ofwhich holds the numbers of some of his co-workers (all of them have his number in their phones as well).

Due to dynamic growth of their operations the board of directors intends to move company headquaters tonew office buildings. It has been decided - in order to boost work efficiency - that every pair of employeesworking in different buildings should know (reciprocally) each others phone number i.e. the memory of theircompany phone ought to hold necessary phone numbers.

Simultaneously, the board have decided to rent as many office buildings as possible to ensure good workingconditions. Help the board of Bytel to plan the number of office buildings and their size in accordancewith the aforementioned requirements.

Task

Write a programme which:

reads the description of employees’ phone number lists from the standard input calculates the maximal number of office buildings and their sizes, satisfying board’s conditions writes the outcome to the standard output.

输入格式

The first line of the standard input consists of two integers: nnand mm (2 \le n \le 100\ 0002≤n≤100 000, 1 \le m \le 2\ 000\ 0001≤m≤2 000 000),separated by single spaces, denoting the number of Bytel employees and the number of pairs of employeeswho have their numbers in company phones, respectively. The employees are numbered from 11 to nn.

Each of the following mm lines contains a single pair of integers a_ia**i and b_ib**i (1 \le a_i < b_i \le n1≤a**i<b**in for 1 \le i \le m1≤im), separated by a single space, denoting that employees a_ia**i and b_ib**i have their numbers (reciprocally) in company phones’ memory. Each pair of integers denoting a pair of employees shall occur once at the most in the standard input.

输出格式

The first line of the standard output should contain a single integer: the maximal number of office buildingsthat Bytel should rent. The second should contain a non-decreasing sequence of positive integers, separatedby singe spaces, denoting the sizes of the office buildings (i.e. the numbers of employees working there).

Should there exist more than one correct answer - write out any one of them.

题意:

现在有一张无向图图,将图中的节点分为若干组,要求任意两组之间的两个点之间要有边。求最多能分成多少组,以及每组的数量。

题解:

不同分组中的两个点直接必须要有边,转换一下就是两个节点之间没有边,那么这两个节点必定在同一个分组中。

所以题目转化为求反图的连通块的个数及大小。

这题的数据范围 n ≤ 1 0 5 n \leq 10^5 n105 。搜索的复杂度就是 O ( n 2 ) O(n^2) O(n2) ,显然行不通。

bfs的时候利用并查集将已经归入连通块的节点进行无效化处理。这样越到后面,搜索的分支会越来越少,因为可选的节点越来越少了。

代码:

#include<iostream>
#include<cstdio>
#include<vector>
#include<queue>
#include<cmath>
#include<map>
#include<cstring>
#include<algorithm>
#define fi first
#define se second
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int INF = 0x3f3f3f3f;
const int N = 2e5 + 10;
int n, m, f[N], ans, cnt[N], vis[N];
vector<int> G[N];
int Find(int x) {return f[x] == x ? x : f[x] = Find(f[x]); } 
void bfs(int st) {
    queue<int> q;
    q.push(st);
    cnt[++ans] = 1;
    while (q.size()) {
        int x = q.front(); q.pop();
        f[x] = Find(x + 1);  //当前节点已经并入其他连通块了,将这个点并到其他的节点上去,是这个节点无效化
        for (int i = 0; i < (int)G[x].size(); i++)
            vis[G[x][i]] = x;  //将x所连的点全部标记
        for (int i = Find(1); i <= n; i = Find(i + 1)) { //遍历当前剩下的节点(直接用find可以减少)
            if (vis[i] != x) {  //当前这个节点与x节点不相连,那么i节点与x必定是一组的,然后对该点无效化处理
                cnt[ans]++;
                f[i] = Find(i + 1); //无效化处理
                q.push(i);
            }
        }
    }
}
int main () {
    scanf("%d%d", &n, &m);
    for (int i = 0; i < m; i++) {
        int u, v;
        scanf("%d%d", &u, &v);
        G[u].push_back(v);
        G[v].push_back(u);
    }
    for (int i = 1; i <= n + 1; i++) //多一个点,用于跳出循环
        f[i] = i;
    ans = 0;
    for (int i = 1; i <= n; i = Find(i + 1))
        bfs(i);
    
    sort(cnt + 1, cnt + ans + 1);
    printf("%d\n", ans);
    for (int i = 1; i <= ans; i++) {
        printf("%d ", cnt[i]);
    }
    printf("\n");
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值