题目大意:
FGD开办了一家电话公司。他雇用了N个职员,给了每个职员一部手机。每个职员的手机里都存储有一些同事的电话号码。由于FGD的公司规模不断扩大,旧的办公楼已经显得十分狭窄,FGD决定将公司迁至一些新的办公楼。FGD希望职员被安置在尽量多的办公楼当中,这样对于每个职员来说都会有一个相对更好的工作环境。但是,为了联系方便起见,如果两个职员被安置在两个不同的办公楼之内,他们必须拥有彼此的电话号码。
思路:
直接考虑哪些人必须要在一个办公室的话,题目的问题就简化成了在这个图的补图内寻找连通块的个数。
暴力地寻找连通块复杂度是
n
2
n^2
n2的,但是冷静分析一下其实并不是这一回事,如果我们每一次广搜到一个点并且将它从未确定归属的集合内删除,以后不再便历这个点,那么一个点被便历的次数将=(并不能将它加入某个连通块的便历的次数+最后一次刚好加入某个连通块的那一次便历)。
不难发现复杂度的瓶颈主要在前面那一个部分,同时也可以发现只有当这条边存在于图中的时候,这次便历才是没有用处的,所以这样的无用的便历次数的总和最多为m,所以不难得出最终的复杂度是
Θ
(
n
+
m
)
\Theta(n+m)
Θ(n+m)。
#include<bits/stdc++.h>
#define REP(i,a,b) for(int i=a,i##_end_=b;i<=i##_end_;++i)
typedef long long ll;
using namespace std;
void File(){
freopen("bzoj1098.in","r",stdin);
freopen("bzoj1098.out","w",stdout);
}
template<typename T>void read(T &_){
T __=0,mul=1; char ch=getchar();
while(!isdigit(ch)){
if(ch=='-')mul=-1;
ch=getchar();
}
while(isdigit(ch))__=(__<<1)+(__<<3)+(ch^'0'),ch=getchar();
_=__*mul;
}
const int maxn=1e5+10;
const int maxm=2e6+10;
int n,m,q,sz,tmp[maxn],tot_tmp,ans,lst[maxn];
vector<int>to[maxn];
set<int>st;
set<int>::iterator it;
queue<int>qu;
void init(){
read(n); read(m);;
int u,v;
REP(i,1,m){
read(u); read(v);
to[u].push_back(v);
to[v].push_back(u);
}
REP(i,1,n)sort(to[i].begin(),to[i].end());
}
bool judge(int u,int v){
if(to[u].size()>to[v].size())swap(u,v);
int p=lower_bound(to[u].begin(),to[u].end(),v)-to[u].begin();
if(p==(int)to[u].size() || to[u][p]!=v)return true;
return false;
}
void work(){
REP(i,1,n)st.insert(i);
while(!st.empty()){
++ans;
int u=*st.begin();
st.erase(u);
qu.push(u);
while(!qu.empty()){
u=qu.front(); qu.pop();
++lst[ans];
tot_tmp=0;
for(it=st.begin();it!=st.end();++it){
int v=*it;
if(judge(u,v))tmp[++tot_tmp]=v;
}
REP(i,1,tot_tmp){
st.erase(tmp[i]);
qu.push(tmp[i]);
}
}
}
printf("%d\n",ans);
sort(lst+1,lst+ans+1);
REP(i,1,ans)printf("%d ",lst[i]);
}
int main(){
File();
init();
work();
//cerr<<(double)clock()/CLOCKS_PER_SEC<<endl;
return 0;
}