bzoj1098: [POI2007]办公楼biu

做法就是任选一个人新建一个楼子扔它进去,然后把它没有号码的所有人和它放在一个办公楼,其他人也这样操作(就是宽搜),扔无可扔为止

但是这样每次枚举复杂度很高,考虑开一个全局的链表,已经有楼子住的人就删掉。

那么如何快速判断是否有号码呢?我们可以开一个bool数组,每次宽搜到当前点就先把这些有号码的点打上标记,结束再清空,从而保证O(n+m)的复杂度

#include<cstdio>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
using namespace std;

struct node
{
    int x,y,next;
}a[4100000];int len,last[110000];
void ins(int x,int y)
{
    len++;
    a[len].x=x;a[len].y=y;
    a[len].next=last[x];last[x]=len;
}
int n,m,aslen,as[110000],L[110000],R[110000];
int list[110000];bool v[110000],b[110000];
void bfs(int st)
{
    int head=1,tail=2;list[1]=st;
    while(head!=tail)
    {
        int x=list[head];head++;
        v[x]=true;
        for(int k=last[x];k;k=a[k].next)b[a[k].y]=true;
        for(int y=R[0];y<=n;y=R[y])
            if(v[y]==false&&b[y]==false)
                R[L[y]]=R[y],L[R[y]]=L[y],list[tail++]=y;
        for(int k=last[x];k;k=a[k].next)b[a[k].y]=false;
    }
    as[++aslen]=tail-1;
}
int main()
{
    int x,y;
    scanf("%d%d",&n,&m);
    len=0;memset(last,0,sizeof(last));
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d",&x,&y);
        ins(x,y),ins(y,x);
    }
    L[0]=0;R[0]=1;
    for(int i=1;i<=n;i++)L[i]=i-1,R[i]=i+1;
    L[n+1]=n;R[n+1]=n+1;
    
    memset(v,false,sizeof(v));
    for(int i=1;i<=n;i++)
        if(v[i]==false)bfs(i);
    sort(as+1,as+aslen+1);
    printf("%d\n",aslen);
    for(int i=1;i<aslen;i++)printf("%d ",as[i]);
    printf("%d\n",as[aslen]);
    return 0;
}

 

posted @ 2018-10-23 19:23  AKCqhzdy  阅读(156)  评论(0编辑  收藏  举报