bzoj1098[POI2007]办公楼biu
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号办公楼。
Source
这道题说简单也不简单,说难也不难,仔细想还是可以做出来的
网上有图论的写法,但是我没用
其实随随便便就可以想到,枚举每个人,把他能联系的人都放到另一个办公楼里,不断地去放,能联系到的如果在同一个办公楼就再放到另一个办公楼
看似问题解决了,但是sorry,错了
先不说算法实现有多麻烦,按照这个做法,如果找到一个人与其他人都不能联系,这个整个框架都得gg
其实正确的做法和之前的区别不大,就是先取一个人,将这个人不能联系到的都放到自己这个办公楼里,把自己办公楼不能联系到的人全部找出来,在原本的序列中把这些人删掉,然后再在删除了那些人后的序列中取一个人,重复这个操作
至于这个做法的正确性,应该是很容易看出来的,我们在序列中取的人是肯定可以联系到之前处理过的办公楼的任意一个人的,处理的次数就是办公楼的数量,至于每个办公楼的人数,在处理的过程中就可以处理出来了
序列的处理可以用队列
代码如下:
1 #include<cstdio> 2 #include<algorithm> 3 using namespace std; 4 int ans,n,m,w[100001],q[100001],cnt,a[100001],h[4000001],nxt[4000001],pre[4000001],vis[100001]; 5 void add(int x,int y) 6 { 7 pre[++cnt]=y; 8 nxt[cnt]=h[x]; 9 h[x]=cnt; 10 } 11 int main() 12 { 13 scanf("%d%d",&n,&m); 14 for(int i=1,x,y;i<=m;i++) 15 scanf("%d%d",&x,&y),add(x,y),add(y,x); 16 for(int i=1;i<=n;i++)q[i]=i; 17 int now=n,l; 18 while(now) 19 { 20 ans++; 21 w[l=1]=q[now];now--; 22 while(l) 23 { 24 a[ans]++; 25 int ca=w[l--]; 26 for(int i=h[ca];i;i=nxt[i])vis[pre[i]]=ca; 27 int now1=now;now=0; 28 for(int i=1;i<=now1;i++) 29 { 30 int g=q[i]; 31 if(vis[g]!=ca)w[++l]=g; 32 else q[++now]=g; 33 } 34 } 35 } 36 printf("%d\n",ans); 37 sort(a+1,a+ans+1); 38 for(int i=1;i<=ans;i++)printf("%d ",a[i]); 39 }