办公楼[POI2007]

题目描述

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

输入

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

输出

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

样例输入

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

样例输出

3
1 2 4

提示

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

 

题解

    考试时打的是并查集,今天上午仿佛中了并查集的毒,哪哪都打的并查集;骗了60分,剩下的是TLE。其实并查集的思路相当科学,只不过是会T掉大数据= =。很明显是要构造反图,但大概是思路不够宽的原因没有刻意想这事,不过也确实造不出来。我的做法是很暴力地枚举不连通的点,然后加权并查集统计各个集合中的点数,O(n^2)大概能过4*10^4以下的点。
    正解只不过是用了链表优化上述思路,而把并查集统计换成了BFS。原来一直以为链表就是邻接表,今天才知道好像只有名字像而已吧。链表用一个nxt[sj]和一个pre[sj]双向连接,删除操作是
         nxt[pre[i]]=nxt[i];   pre[nxt[i]]=pre[i];
这样每枚举一个点就把它反图所在连通块全部BFS完(用队列,不知道为什么cena会栈溢出,内网上没事),顺便把所有入队点都从链表里删掉,下一次只从链表中剩下的元素里枚举,时间优化不是一点半点。ad学长问大家一个算法有没有学懂都是问时间复杂度,可见这确实能衡量对算法的理解程度。我至今算时间复杂度也不过是数数for循环,今天看着学长分析高级算法里的复杂度神乎其技。说来很惭愧原来学的那些数学知识早就忘干净了,欧拉函数扩展gcd只能说一句“会过”,怎么能行呢。今天的课也很需要消化,不练题肯定不行。在教室就是学习数学的好时间,悟已往之不谏,知来者之可追。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
const int nj=100010,mj=2000010;
int n,m,a1,a2,ans[nj],h[nj],e,jk,temp,nxt[nj],pre[nj];
bool vi[nj],bg[nj];
struct B
{
    int v,ne;
}b[mj*2];
queue<int> q;
void add(int x,int y)
{
     b[e].v=y;
     b[e].ne=h[x];
     h[x]=e++;
}
void bfs(int x)
{
     memset(vi,0,sizeof(vi));
     vi[x]=1;
     for(int i=h[x];i!=-1;i=b[i].ne)
        vi[b[i].v]=1;
     for(int i=1;i<=n;i=nxt[i])
       if(!vi[i]&&!bg[i])
       {
          q.push(i);
          nxt[pre[i]]=nxt[i];
          pre[nxt[i]]=pre[i];
       }
     while(!q.empty())
     {
        temp=q.front();
        q.pop();
        bg[temp]=1;
        bfs(temp);
        ans[jk]++;
     }
}
int main()
{
    //freopen("t3.txt","r",stdin);
    //freopen("biu.in","r",stdin);
    //freopen("biu.out","w",stdout);
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)  
    {
         pre[i]=i-1;
         nxt[i]=i+1;
    }
    memset(h,-1,sizeof(h));
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d",&a1,&a2);
        add(a1,a2);
        add(a2,a1);
    }
    for(int i=1;i<=n;i=nxt[i])  
    {
        jk++;
        bg[i]=1;
        ans[jk]++;
        bfs(i);
        nxt[pre[i]]=nxt[i];
        pre[nxt[i]]=pre[i];
    }
    printf("%d\n",jk);
    sort(ans+1,ans+jk+1,less<int>());
    for(int i=1;i<=jk;i++)
      printf("%d ",ans[i]);
    //while(1);
    return 0;
}

 

 
posted @ 2017-07-15 15:34  moyiii  阅读(277)  评论(0编辑  收藏  举报