[bzoj1098][POI2007]办公楼biu

来源:互联网 发布:java怎么学才能学得好 编辑:程序博客网 时间:2024/04/29 02:50

      这道题考试的时候就用邻接矩阵求反图再dfs,弄了50分,看来是我太菜了。看正解是用并查集瞎搞,本人还没有达到这个地步,于是去网上借鉴了一下,用bfs+链表。顺便学习了一下链表。

     由题目可知,互相没有电话号码的人必须在一个楼里,所以问题就转化成了求原图的反图的联通块的个数以及大小。由于数据限制,n^2是行不通的。bfs+链表的时间复杂度是O(n+m)。

      我们先把所有的节点挂链,然后再把链表上的一个节点入队,遍历其在原图上相邻的点并做上标记,那么这时没有打上标记的点在补图上和当前节点一定有边相连因而一定在同一个联通块中,所以再把这些没有打上标记的点入队,并且在链表中除去,继续这个过程,直到队列为空时这个联通块就找出来了。把链接的点再删除标记,再取链表上还存在的点入队寻找一个新的联通块,直到删掉所有点为止,复杂度降为了O(n + m)。

#include<iostream>#include<map>#include<cstdio>#include<cstring>#include<cmath>#include<algorithm>#include<vector>using namespace std;#define pos(i,a,b) for(int i=(a);i<=(b);i++)#define pos2(i,a,b) for(int i=(a);i>=(b);i--)#define N 1000100int n,m;struct haha{       int to,next;}edge[4*N];int head[N],cnt=1;void add(int u,int v){     edge[cnt].to=v;     edge[cnt].next=head[u];     head[u]=cnt++;}int pre[N],nex[N],q[N],cun[N];int ans;void del(int x){     nex[pre[x]]=nex[x];     pre[nex[x]]=pre[x];}int flag[N];void bfs(int x){     int hea=0,tail=1;     q[0]=x;     while(hea<tail)     {        cun[ans]++;        int tmp=q[hea++];        for(int i=head[tmp];i;i=edge[i].next)        {          int to=edge[i].to;          flag[to]=1;        }        for(int i=nex[0];i<=n;i=nex[i])          if(!flag[i])          {            del(i);            q[tail++]=i;          }        for(int i=head[tmp];i;i=edge[i].next)        {          int to=edge[i].to;          flag[to]=0;        }     }}int main(){    //freopen("biu.in","r",stdin);    //freopen("biu.out","w",stdout);    scanf("%d%d",&n,&m);    pos(i,1,m)    {       int x,y;       scanf("%d%d",&x,&y);       add(x,y);       add(y,x);    }    pos(i,0,n)      nex[i]=i+1;    pos(i,1,n+1)      pre[i]=i-1;    for(int i=nex[0];i<=n;i=nex[0])    {       del(i);       ans++;       bfs(i);    }    printf("%d\n",ans);    sort(cun+1,cun+ans+1);    pos(i,1,ans)      printf("%d ",cun[i]);    while(1);    return 0;}