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

题目分析

题意很明确了= = 画出样例后发现就是让你求出反图的联通块个数
但是这点数边数如此巨大 直接建图绝逼炸QAQ
以下是奇技淫巧:先把所有点用链表串起来 然后将链头元素扔进一个队列里 枚举所有这个点到不了的点 扔进队列并且从链表里删除 循环操作直到队列里没有元素 这时 我们就成功找出了反图的一个联通块 循环操作一直到链表被删完为止
大概就是用链表优化了BFS 成功get链表删除新技能
时间复杂度

#include <cstdio>
#include <cstring>
#include <cmath>
#include <queue>
#include <algorithm>
using namespace std;
int n,m,tot;
int pre[100100],nxt[100100];
int head[2001000],to[4001000],net[4001000];
void add(int x,int y)
{
    net[++tot]=head[x];
    head[x]=tot;
    to[tot]=y;
}
int ans[100100];
int cnt,color;
int vis[100100];
void del(int i)
{
    nxt[pre[i]]=nxt[i];
    pre[nxt[i]]=pre[i];
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        add(x,y),add(y,x);
    }
    nxt[0]=1;
    for(int i=1;i<=n;i++)
        nxt[i]=i+1,pre[i]=i-1;
    pre[n+1]=n;
    while(nxt[0]!=n+1)
    {
        queue<int>q;
        cnt=0;
        q.push(nxt[0]);
        del(0);
        while(q.size())
        {
            color++;
            int x=q.front();
            q.pop();
            for(int i=head[x];i;i=net[i])
                vis[to[i]]=color;
            for(int i=nxt[0];i!=n+1;i=nxt[i])
                if(vis[i]!=color) q.push(i),cnt++,del(i);
        }
        ans[++ans[0]]=cnt;
    }
    sort(ans+1,ans+ans[0]+1);
    printf("%d\n",ans[0]);
    for(int i=1;i<=ans[0];i++) printf("%d ",ans[i]);
    return 0;
}

发表评论

邮箱地址不会被公开。 必填项已用*标注