CodeForces - 920E Connected Components? (搜索 数据结构)

本文探讨了一道涉及图论和DSU算法的题目,详细介绍了如何利用STL和优先队列解决最大连通分量数量及各分量节点数的问题。通过分析点对关系,实现了高效搜索和连通性判断。

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

题目链接

前言

A完了发现标签里有一个“dsu”,这个疑问感觉是要留着过年。

参考博客

参考博客

这个博客写了5个版本的代码,深刻揭示了“stl到底还有多少我不会用的东西”这个话题。

正文

一、题意

一张图中有n个点。给出m个点对,每个点对由x,y两个数字组成,表示x,y之间没有边相连。若x,y(或y,x)点对没有出现过,说明x,y之间有边存在。n,m不超过200000,同时m不超过(n + 1) * n / 2。

求出最大连通分量的数量,以及每个连通分量中节点的个数。

二、思路

对每个节点进行搜索。用vector维护“未被访问过的点”,搜索时寻找和当前节点有边相连的点,进行访问,访问过就从容器中删除。“寻找和当前节点有边相连的点”这个操作,只要遍历容器中所有剩下的点,与当前点有点对,则跳过,否则就是“有边相连”。

这么一想,如果一条边都没有,复杂度岂不是o(n * n)直接炸?

欸,当然不会炸。m是有上限的,当一条边都没有的时候,(n + 1) * n / 2还不到200000,即使n * n的复杂度又如何?

而实际上,这样的做法,会重复进行的操作只有o(m + n)次。因为只有m个点对,我们会跳过而导致重复访问的只能是O(m)次。而当我们对某个点不跳过时,它就会从容器中被删除。在对容器中的点顺序尝试搜索的时候,最终搜索的复杂度首先是“每个点都会被搜到一次”,这是o(n)的复杂度,然后是“有一些点会重复地被搜到”,这里是o(m)的复杂度。

三、代码

#include <bits/stdc++.h>
using namespace std;

const int mx = 2e5 + 10;
int n, m;
map<int, bool> mp[mx];
vector<int> s;
priority_queue<int, vector<int>, greater<int> > ans;

void bfs(int x) {
	// cout << "go bfs" << endl;
	queue<int> q;
	q.push(x);
	swap(s[0], s.back());
	s.pop_back();
	int num = 0;
	while(!q.empty()) {
		num++;
		int cur = q.front(); q.pop();
		// cout << "bfs x = " << x << " cur = " << cur << endl;
		for (int i = 0; i < s.size(); i++) {
			if(!mp[cur].count(s[i])) {//cur到s[i]存在边
				q.push(s[i]);
				//通过这样的方法实现从容器中删元素,更快
				swap(s[i], s.back());
				s.pop_back();
				i--;
			}
		}

		// for (int i = 1; i <= n; i++) {
		// 	if (s.count(i)) { //找到未访问的i
		// 		if (!mp[cur].count(i)) { //如果cur到i存在边连接
		// 			s.erase(i); //把i归为未访问
		// 			q.push(i); //继续搜i,找到和i相连的点
		// 		}
		// 	}
		// }
	}
	ans.push(num);
}


int main() {
	scanf("%d %d", &n, &m);
	while(m--) {
		int x, y;
		scanf("%d %d", &x, &y);
		mp[x][y] = true;
		mp[y][x] = true;
	}
	for (int i = 1; i <= n; i++)
		s.push_back(i);

	//搜索,记录每个点形成连通块的大小
	while(!s.empty()) {
		bfs(s[0]);
	}

	//输出答案
	printf("%d\n", ans.size());
	while(!ans.empty()) {
		printf("%d", ans.top());
		ans.pop();
		if(ans.empty()) printf("\n");
		else
			printf(" ");
	}
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值