BZOJ1098:[POI2007]办公楼biu 补图+链表+BFS
题目大意:
给定一个无向图,求出补图连通块的数目以及每一块的大小,按照从小到大的顺序输出.
思路:
首先由题意显然能够转化为上述问题.
在一个补图连通块中的所有节点必定在一个块中,由于要使得块数最多,不在同一补图连通块中的点显然也不能分到一起.
因此每一个补图连通块就是一个办公楼.
接下来考虑如何求.直接转化为补图是\(O(n^2)\)的.
我们考虑维护一个链表,每次寻找一个连通块时,首先选择链表中的一个元素删去并加入队列,找到链表中的与这个元素(在原图中)不相邻的所有元素,均在链表中删除并加入队列.直到队列为空我们就找到了一个连通块.
由于在链表中的每一个元素只会被删去一次,原图中的每一条边也只会被遍历一次,因此总的时间复杂度为\(O(n+m)\).
#include<cstdio>
#include<cstring>
#include<cctype>
#include<climits>
#include<iostream>
#include<algorithm>
using namespace std;
#define N 100010
#define M 2000010
int head[N],next[M<<1],end[M<<1];
inline void addedge(int a,int b){static int q=1;end[q]=b,next[q]=head[a],head[a]=q++;}
inline void make(int a,int b){addedge(a,b),addedge(b,a);}
int l[N],r[N];
int ans[N],cnt;
int q[N],fr,ta;
int vis[N],tclock;
int main(){
#ifndef ONLINE_JUDGE
freopen("tt.in","r",stdin);
#endif
int n,m;scanf("%d%d",&n,&m);
int a,b;while(m--)scanf("%d%d",&a,&b),make(a,b);
register int i,j;
for(i=1;i<n;++i)l[i]=i-1,r[i]=i+1;
l[0]=n,r[0]=1,l[n]=n-1,r[n]=0;
while(r[0]){
fr=ta=0;q[ta++]=r[0];l[r[r[0]]]=0,r[0]=r[r[0]];
while(fr^ta){
++tclock;
for(j=head[i=q[fr++]];j;j=next[j])vis[end[j]]=tclock;
for(j=r[0];j;j=r[j])if(vis[j]!=tclock)q[ta++]=j,r[l[j]]=r[j],l[r[j]]=l[j];
}ans[++cnt]=ta;
}
sort(ans+1,ans+cnt+1);
printf("%d\n",cnt);for(i=1;i<=cnt;++i)printf("%d ",ans[i]);
return 0;
}
评论 (0)