【CodeFores- 920 E 】Connected Components? 【求补图的联通快 链表+BFS 优化】

本文介绍了一种求解补图联通块的算法,通过遍历无关边进行标记,并利用广度优先搜索(BFS)寻找相关顶点,最终统计各联通块的大小。文章详细解释了算法原理与实现细节。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

You are given an undirected graph consisting of n vertices and edges. Instead of giving you the edges that exist in the graph, we give you m unordered pairs (x, y) such that there is no edge between x and y, and if some pair of vertices is not listed in the input, then there is an edge between these vertices.

You have to find the number of connected components in the graph and the size of each component. A connected component is a set of vertices X such that for every two vertices from this set there exists at least one path in the graph connecting these vertices, but adding any other vertex to X violates this rule.

Input
The first line contains two integers n and m (1 ≤ n ≤ 200000, ).

Then m lines follow, each containing a pair of integers x and y (1 ≤ x, y ≤ n, x ≠ y) denoting that there is no edge between x and y. Each pair is listed at most once; (x, y) and (y, x) are considered the same (so they are never listed in the same test). If some pair of vertices is not listed in the input, then there exists an edge between those vertices.

Output
Firstly print k — the number of connected components in this graph.

Then print k integers — the sizes of components. You should output these integers in non-descending order.

Example
Input
5 5
1 2
3 4
3 2
4 2
2 5
Output
2
1 4

分析: 求补图的 联通快,感觉很好写,但是仔细看看真的是无从下手,以为是带权并查集,但是却找不到关系。
看了题解 明白了,其实原理很简单,就是求补图的联通快。对于一个点u来说,原图中边对应的是无关的边,但是有关的边呢? 我们正常做联通快的时候是不是遍历的是和u有关的边,所以我们标记好每次 访问的无关的点,然后再遍历所有点(当然每次都遍历所有的点肯定不行的,所以我们将入队过的点都删除,因为每个点只会入队一次 ,这样就会大大减少时间复杂度)找到未标记的点,这些未标记的点不就是和u有关,我们正常当作bfs来求就行了。

#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define ULL unsigned long long

const int N = 200000+11;
const int M = 3e5+11;
const int inf =0x3f3f3f3f;
const int mod = 1e9+7;
const int inff = 0x3f3f3f3f3f3f3f3f;

int read(){
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); }
    while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3) +ch-'0';ch=getchar();}
    return x*f;
}

/*-------------------------------------*/

vector<int>ve[N];  // 存原图
list<int>L;
list<int>:: iterator it,itt;
vector<int>as; // 记录每个联通快的大小
map<int,bool>vis; // 标记数组

int main(){
    int n,m;scanf("%d%d",&n,&m);
    while(m--){
        int x,y;scanf("%d%d",&x,&y);
        ve[x].push_back(y);
        ve[y].push_back(x);
    }
    for(int i=1;i<=n;i++) L.push_back(i);
    int ans=0;
    while(!L.empty()){  // 每次就是一个联通快
        ans++;  it=L.begin(); int u=(*it);  L.erase(it);
        int cnt=0;
        queue<int>Q; Q.push(u); cnt++;
        while(!Q.empty()){
            int now=Q.front(); Q.pop(); vis.clear();
            for(int i=0;i<ve[now].size();i++){ //  标记与 now 无关的点
                int v=ve[now][i];
                vis[v]=true;
            }
            for(it=L.begin();it!=L.end();){ // 那么所有顶点中 没有标记过的 不就是和now有关系的边,正常做bfs就好。
                if(!vis[(*it)]){
                    Q.push((*it)); cnt++;
                    itt=it; it++; L.erase(itt);  // 一个点只会出现在一个联通快中,只会入队一次,所以用过之后直接删除就行了,
                }else it++;
            }
        }
        as.push_back(cnt);
    }

    sort(as.begin(),as.end());
    printf("%d\n",ans);
    for(int i=0;i<as.size();i++){
        if(i!=0) putchar(' ') ;
        printf("%d",as[i]);
    }
    puts("");
return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值