bzoj 1098: [POI2007]办公楼biu (补图+链表优化bfs)

来源:互联网 发布:linux 时间戳 编辑:程序博客网 时间:2024/05/16 16:23

1098: [POI2007]办公楼biu

Time Limit: 20 Sec  Memory Limit: 162 MB
Submit: 1204  Solved: 558
[Submit][Status][Discuss]

Description

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

Input

  第一行包含两个整数N(2<=N<=100000)和M(1<=M<=2000000)。职员被依次编号为1,2,……,N.以下M行,每
行包含两个正数A和B(1<=A

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号办公楼。

Source

[Submit][Status][Discuss]

题解:补图+链表优化bfs.

昨天查了一些关于补图的东西,再看这道题发现其实就是求补图的连通分量的个数。

所谓补图,就是将已有的边删去,没有的边加上。这样补图中有连边的两个点就是不彼此拥有电话号码的人,那么他们一定要在一栋办公楼里。

但是这个题的点数很多,而原图的边却很少。所以我们无法建立补图,需要在原图的基础上求补图的连通分量的大小及个数。

我们考虑一个点,有哪些点需要和他在一个连通分量中,与他有边相连的点必然不用,那么我们可以把与他相连的点标记,每次找出未标记且未分配的点加入队列,更新答案并进行扩展。注意每次做完之后要清相连点的标记,因为当前点的关系可以使两个人不在同一个楼办公,但是新加入队列中的点有可能需要这些点在同楼办公。

这样还是不能在科学的时间内出解。于是就出现了链表优化bfs。因为每次点都只能属于一个楼,那么我们用链表维护剩余的未分配的点,如果我们为一个点分配了办公楼,就将其从链表中删去,有效的减少了每个点被枚举的次数。

时间复杂度O(n+m)

#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>#include<cmath>#include<queue>#define N 4000003using namespace std;int vis[N],n,m,num[N],ans;int point[N],next[N],v[N],tot,l[N],r[N],mark[N];void add(int x,int y){tot++; next[tot]=point[x]; point[x]=tot; v[tot]=y;tot++; next[tot]=point[y]; point[y]=tot; v[tot]=x;//cout<<x<<" "<<y<<endl;}void del(int x){vis[x]=1;r[l[x]]=r[x];l[r[x]]=l[x];}void bfs(int x){queue<int> p; p.push(x);while(!p.empty())    {    int now=p.front(); p.pop();     num[ans]++;    for (int i=point[now];i;i=next[i]) mark[v[i]]=1;    for (int i=r[0];i;i=r[i])     if (!vis[i]&&!mark[i]) del(i),p.push(i);    for (int i=point[now];i;i=next[i]) mark[v[i]]=0;}}int main(){freopen("a.in","r",stdin);scanf("%d%d",&n,&m);for (int i=1;i<=m;i++) { int x,y; scanf("%d%d",&x,&y); add(x,y); }for (int i=0;i<=n;i++) l[i]=i-1,r[i]=i+1;r[n]=0;for (int i=1;i<=n;i++) if(!vis[i]) { ans++;  del(i); bfs(i); }sort(num+1,num+ans+1);printf("%d\n",ans);for (int i=1;i<=ans;i++) printf("%d ",num[i]);printf("\n");}


0 0
原创粉丝点击