BZOJ1098 POI2007 办公楼biu 【链表+bfs】

时间:2023-03-10 07:28:02
BZOJ1098 POI2007 办公楼biu 【链表+bfs】

Description

FGD开办了一家电话公司。他雇用了N个职员,给了每个职员一部手机。每个职员的手机里都存储有一些同事的电话号码。由于FGD的公司规模不断扩大,旧的办公楼已经显得十分狭窄,FGD决定将公司迁至一些新的办公楼。FGD希望职员被安置在尽量多的办公楼当中,这样对于每个职员来说都会有一个相对更好的工作环境。但是,为了联系方便起见,如果两个职员被安置在两个不同的办公楼之内,他们必须拥有彼此的电话号码。

Input

第一行包含两个整数N(2<=N<=100000)和M(1<=M<=2000000)。职员被依次编号为1,2,……,N.以下M行,每行包含两个正数A和B(1<=A<b<=n),表示职员a和b拥有彼此的电话号码),li <= 1000

Output

包含两行。第一行包含一个数S,表示FGD最多可以将职员安置进的办公楼数。第二行包含S个从小到大排列的数,每个数后面接一个空格,表示每个办公楼里安排的职员数。

Sample Input

7 16

1 3

1 4

1 5

2 3

3 4

4 5

4 7

4 6

5 6

6 7

2 4

2 7

2 5

3 5

3 7

1 7

Sample Output

3

1 2 4

HINT

FGD可以将职员4安排进一号办公楼,职员5和职员7安排进2号办公楼,其他人进3号办公楼。


思路

容易发现这个问题就是求一个图里边少多能分成多少个块,使得每一个块中间都没有边相连

正向不好求,然后就考虑在补图上求联通块个数

然后就考虑用bfs的方法,但直接bfs时间效率是\(O(nm)\)的

所以可以用链表优化

这样当一个点被删除才会访问连接的边,所以时间效率就变成了\(O(n+m)\)


这个链表用法很神奇


#include<bits/stdc++.h>
using namespace std;
#define fu(a,b,c) for(int a=b;a<=c;++a)
#define fd(a,b,c) for(int a=b;a>=c;--a)
#define N 1000010
struct Block{int l,r,vis,mark;}b[N];
vector<int> g[N];
int n,m,ans=0,siz[N];
void add(int u,int v){
g[u].push_back(v);
g[v].push_back(u);
}
void del(int id){
b[b[id].l].r=b[id].r;
b[b[id].r].l=b[id].l;
b[id].vis=1;
}
void solve(int s){
queue<int> q;q.push(s);
del(s);
while(!q.empty()){
int u=q.front();q.pop();
siz[ans]++;
fu(i,0,(signed)g[u].size()-1)b[g[u][i]].mark=1;
for(int i=b[0].r;i;i=b[i].r)
if(!b[i].vis&&!b[i].mark)q.push(i),del(i);
fu(i,0,(signed)g[u].size()-1)b[g[u][i]].mark=0;
}
}
int main(){
scanf("%d%d",&n,&m);
fu(i,1,m){
int u,v;
scanf("%d%d",&u,&v);
add(u,v);
}
b[0].r=1;
fu(i,1,n)b[i].l=i-1,b[i].r=i+1;
b[n].r=0;
fu(i,1,n)if(!b[i].vis)++ans,solve(i);
sort(siz+1,siz+ans+1);
printf("%d\n",ans);
fu(i,1,ans)printf("%d ",siz[i]);
return 0;
}