记原图的补图中每个点的度数为
d
i
d_i
di 。
先找出
d
i
d_i
di 最小的点,将与其有边相连的点作为一个连通块,其他每个点单独作为一个连通块。暴力枚举
2
2
2 个连通块,再枚举其中的点,查询是否在原图中有边,有边则合并。
这样显然是正确的,然后就是复杂度的问题。
由于刚开始选的是
d
i
d_i
di 最小的点,所以
d
i
≤
⌊
2
m
n
⌋
d_i \le \lfloor{2m\over n}\rfloor
di≤⌊n2m⌋
考虑第
1
1
1 个连通块与其他连通块的枚举,枚举次数为
O
(
n
⌊
2
m
n
⌋
)
O(n\lfloor{2m\over n}\rfloor)
O(n⌊n2m⌋) ,即
O
(
m
)
O(m)
O(m) 。
考虑其他连通块间的枚举,由于总连通块数
≤
⌊
2
m
n
⌋
+
1
\le \lfloor{2m\over n}\rfloor+1
≤⌊n2m⌋+1 ,次数为
O
(
⌊
2
m
n
⌋
2
)
O(\lfloor{2m\over n}\rfloor^2)
O(⌊n2m⌋2) 。
而
⌊
m
n
⌋
≤
200000
\lfloor{m\over n}\rfloor\le \sqrt{200000}
⌊nm⌋≤200000 ,总复杂度为
O
(
n
log
n
)
O(n\log n)
O(nlogn) 。
#include<bits/stdc++.h>
using namespace std;
const int N=200010;
map<int,bool>M[N];
vector<int>a[N],g[N];
vector<int>c;
int k,n,m,x,y,t,mn;
int cnt=1;
int f[N],sz[N];
bool b[N];
int find(int x){
return f[x]==x?x:f[x]=find(f[x]);
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
scanf("%d%d",&x,&y);
g[x].push_back(y);g[y].push_back(x);
M[x][y]=M[y][x]=1;
}
mn=n+1;
for(int i=1;i<=n;i++)
if(g[i].size()<mn)mn=g[i].size(),t=i;
for(int i=0;i<g[t].size();i++)b[g[t][i]]=1;
for(int i=1;i<=n;i++)
if(!b[i])a[1].push_back(i);else a[++cnt].push_back(i);
for(int i=1;i<=cnt;i++)f[i]=i;
for(int i=1;i<cnt;i++)
for(int j=i+1;j<=cnt;j++)
if(find(i)!=find(j)){
bool fl=0;
for(int k=0;!fl&&k<a[i].size();k++)
for(int l=0;l<a[j].size();l++)
if(!M[a[i][k]][a[j][l]]){
fl=1;
break;
}
if(fl)f[f[i]]=f[j];
}
for(int i=1;i<=cnt;i++)sz[find(i)]+=a[i].size();
for(int i=1;i<=cnt;i++)
if(find(i)==i)c.push_back(sz[i]);
sort(c.begin(),c.end());
printf("%d\n",c.size());
for(int i=0;i<c.size();i++)printf("%d ",c[i]);
return 0;
}