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;
}
posted @ 2018-09-20 19:50  Dream_maker_yk  阅读(151)  评论(0编辑  收藏  举报