Bzoj1098[POI2007]办公楼biu

传送门

求补图的联通块个数。

补图非常大。

链表维护搜索。

把所有点加到链表中,每次取出链头,删掉,联通块个数++;扔进队列,取出队首,把原图跟他有连边的标记了,遍历一遍链表把没有标记的删除,入队,继续取出队首重复。

RE了半个下午发现建图边开小了。。

//Achen
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<vector>
#include<queue>
#include<ctime>
#include<cmath>
const int N=100007;
const int M=2000007;
typedef long long LL;
using namespace std;
int n,m,o,head,tail,r[N],l[N],tot,sz[N],vis[N];
queue<int>que;

template<typename T> void read(T &x) {
    char ch=getchar(); x=0; T f=1;
    while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
    if(ch=='-') f=-1,ch=getchar();
    for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0'; x*=f;
}

int ecnt,fir[N],nxt[M<<1],to[M<<1];
void add(int u,int v) {
    nxt[++ecnt]=fir[u]; fir[u]=ecnt; to[ecnt]=v;
    nxt[++ecnt]=fir[v]; fir[v]=ecnt; to[ecnt]=u;
}

int main() {
#ifdef DEBUG
    freopen(".in","r",stdin);
    freopen(".out","w",stdout);
#endif
    read(n); read(m);
    for(int i=1;i<=m;i++) {
        int u,v;
        read(u); read(v);
        add(u,v);
    }
    head=0; tail=n+1;
    r[head]=1;
    for(int i=1;i<=n;i++) {
        r[i]=i+1;
        l[i]=i-1;
    }
    l[tail]=n;
    while(r[head]!=tail) {
        tot++;
        int x=r[head];
        l[r[x]]=l[x];
        r[l[x]]=r[x];
        que.push(x);
        sz[tot]++;
        while(!que.empty()) {
            int x=que.front();
            que.pop(); 
            for(int i=fir[x];i;i=nxt[i]) {
                int y=to[i];
                vis[y]=x;
            }
            for(int i=r[head];i!=tail;i=r[i]) {
                if(vis[i]!=x) {
                    sz[tot]++;
                    que.push(i);
                    l[r[i]]=l[i];
                    r[l[i]]=r[i];
                }
            }
        }
    }
    sort(sz+1,sz+tot+1);
    printf("%d\n",tot);
    for(int i=1;i<=tot;i++) printf("%d ",sz[i]);
    puts("");
    return 0;
}
View Code

 

posted @ 2017-12-16 07:17  啊宸  阅读(157)  评论(0编辑  收藏  举报