BZOJ2054:疯狂的馒头&BZOJ2375:疯狂的涂色
BZOJ1483:[HNOI2009]梦幻布丁 链表启发式合并

BZOJ1098:[POI2007]办公楼biu 补图+链表+BFS

shinbokuow posted @ Dec 26, 2014 03:28:07 PM in BZOJ with tags 链表 补图 , 1305 阅读

 

题目大意:

给定一个无向图,求出补图连通块的数目以及每一块的大小,按照从小到大的顺序输出.

思路:

首先由题意显然能够转化为上述问题.

在一个补图连通块中的所有节点必定在一个块中,由于要使得块数最多,不在同一补图连通块中的点显然也不能分到一起.

因此每一个补图连通块就是一个办公楼.

接下来考虑如何求.直接转化为补图是\(O(n^2)\)的.

我们考虑维护一个链表,每次寻找一个连通块时,首先选择链表中的一个元素删去并加入队列,找到链表中的与这个元素(在原图中)不相邻的所有元素,均在链表中删除并加入队列.直到队列为空我们就找到了一个连通块.

由于在链表中的每一个元素只会被删去一次,原图中的每一条边也只会被遍历一次,因此总的时间复杂度为\(O(n+m)\).

#include<cstdio>
#include<cstring>
#include<cctype>
#include<climits>
#include<iostream>
#include<algorithm>
using namespace std;
 
#define N 100010
#define M 2000010
int head[N],next[M<<1],end[M<<1];
inline void addedge(int a,int b){static int q=1;end[q]=b,next[q]=head[a],head[a]=q++;}
inline void make(int a,int b){addedge(a,b),addedge(b,a);}
 
int l[N],r[N];
 
int ans[N],cnt;
 
int q[N],fr,ta;
 
int vis[N],tclock;
int main(){
    #ifndef ONLINE_JUDGE
    freopen("tt.in","r",stdin);
    #endif
 
    int n,m;scanf("%d%d",&n,&m);
    int a,b;while(m--)scanf("%d%d",&a,&b),make(a,b);
 
    register int i,j;
    for(i=1;i<n;++i)l[i]=i-1,r[i]=i+1;
    l[0]=n,r[0]=1,l[n]=n-1,r[n]=0;
 
    while(r[0]){
        fr=ta=0;q[ta++]=r[0];l[r[r[0]]]=0,r[0]=r[r[0]];
        while(fr^ta){
            ++tclock;
            for(j=head[i=q[fr++]];j;j=next[j])vis[end[j]]=tclock;
            for(j=r[0];j;j=r[j])if(vis[j]!=tclock)q[ta++]=j,r[l[j]]=r[j],l[r[j]]=l[j];  
        }ans[++cnt]=ta;
    }
    sort(ans+1,ans+cnt+1);
    printf("%d\n",cnt);for(i=1;i<=cnt;++i)printf("%d ",ans[i]);
 
    return 0;
}

登录 *


loading captcha image...
(输入验证码)
or Ctrl+Enter