BZOJ 1098 [POI2007]办公楼biu(反向图bfs+并查集优化)

时间:2024-04-26 06:52:42

【题目链接】 http://www.lydsy.com/JudgeOnline/problem.php?id=1098

【题目大意】

  现在有一张图,要求将这张图的点划分为尽量多的分组,对于不同分组的两个点
  要求必须存在连边。

【题解】

  不同分组之间的两点必须连边等价于没有连边的点一定在同一分组内,
  所以题目转化为求反图的连通块和其大小,搜索的理论复杂度O(n^2),显然不行,
  bfs的时候对于已经归入其余连通块的点用并查集进行段无效信息处理,减少搜索树的分支,
  显然经过这样的处理搜索分支的数量下降得非常快,就能顺利解决此题了。

【代码】

#include <cstdio>
#include <algorithm>
#include <vector>
#include <queue>
using namespace std;
const int N=100010;
int f[N],n,m,vis[N],ans,cnt[N];
vector<int> v[N];
int sf(int x){return f[x]==x?x:f[x]=sf(f[x]);}
void bfs(int st){
queue<int> q;
q.push(st);
cnt[++ans]=1;
while(q.size()){
int x=q.front();q.pop();f[x]=sf(x+1);
for(int i=0;i<v[x].size();i++)vis[v[x][i]]=x;
for(int i=sf(1);i<=n;i=sf(i+1))if(vis[i]!=x){
cnt[ans]++;
f[i]=sf(i+1);
q.push(i);
}
}
}
int main(){
while(~scanf("%d%d",&n,&m)){
ans=0;
while(m--){
int x,y;
scanf("%d%d",&x,&y);
v[x].push_back(y);
v[y].push_back(x);
}
for(int i=1;i<=n+1;i++)f[i]=i;
for(int i=1;i<=n;i=sf(i+1))bfs(i);
printf("%d\n",ans);
sort(cnt+1,cnt+ans+1);
for(int i=1;i<=ans;i++)printf("%d ",cnt[i]);
puts("");
}return 0;
}