一、题目
二、题目链接
http://codeforces.com/contest/920/problem/E
三、题意
给定一个$N$和$M$。$N$表示有$N$个点,$M$表示,在一个$N$个点组成的无向完全图中,接下来的$M$条无向边不存在。
问你在这个图中有多少个连通分量,并且从小到大输出每个连通分量的顶点个数。
四、思路
求无向图的连通分量个数,只需要跑一边bfs就OK了。其实dfs也可以,只是太多的“函数压栈现场保护”浪费一丢丢时间而已,慢一点点,影响其实并不大。
要注意的是,无论跑bfs还是dfs,不能枚举边,因为这题的边数太多了,会超时。只能枚举点,枚举所有未访问的点。
然后,在跑bfs的过程中,统计这次bfs访问的点的个数,返回就OK了。枚举每一个点,如果是未访问的,就跑一遍bfs,记录这次bfs访问的点的个数,问题就解决了。
五、源代码
#pragma GCC optimize(2) #pragma comment(linker, "/STACK:102400000, 102400000") #include<bits/stdc++.h> using namespace std; typedef long long LL; const int MAXN = 2e5 + 10; template <class T> inline void read(T &x) { int t; bool flag = false; while((t = getchar()) != ‘-‘ && (t < ‘0‘ || t > ‘9‘)) ; if(t == ‘-‘) flag = true, t = getchar(); x = t - ‘0‘; while((t = getchar()) >= ‘0‘ && t <= ‘9‘) x = x * 10 + t - ‘0‘; if(flag) x = -x; } int n, m; unordered_set<int> g[MAXN], invis; vector<int> ans, tmp; queue<int> que; int bfs(int s) { int res = 1; while(!que.empty())que.pop(); que.push(s); invis.erase(s); while(!que.empty()) { int t = que.front(); que.pop(); tmp.clear(); for(auto x : invis) {//注意不要边迭代、边移除。 if(g[t].count(x))continue; else { que.push(x); tmp.push_back(x); res++; } } for(auto x : tmp)invis.erase(x); } return res; } void init() { invis.clear(); for(int i = 1; i <= n; ++i)invis.insert(i); for(int i = 0; i < MAXN; ++i)g[i].clear(); ans.clear(); } int main() { #ifndef ONLINE_JUDGE freopen("Einput.txt", "r", stdin); #endif // ONLINE_JUDGE int a, b; scanf("%d%d", &n, &m); init(); for(int i = 0; i < m; ++i) { read(a), read(b); g[a].insert(b); g[b].insert(a); } for(int i = 1; i <= n && invis.size() > 0; ++i) { if(invis.count(i))ans.push_back(bfs(i)); } sort(ans.begin(), ans.end()); printf("%d\\n", ans.size()); for(auto x : ans)printf("%d ", x); return 0; }