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;
}