1. 程式人生 > >[POI2007]辦公樓Biu

[POI2007]辦公樓Biu

bzoj1098

對於每個對點,如果他倆沒有直接連邊,就必須放在一個樓裡,所以顯而易見的就是要求補圖的聯通塊。
但是沒辦法直接把補圖建出來。
可以用連結串列來維護之間沒有訪問過的集合。
每次從未訪問的點中選一個,找沒訪問的集合中不與他相連的點有哪些,然後刪去這些點,表示已經訪問過了,並且繼續找他們沒有訪問過的節點即可。

#include <iostream>
#include <queue>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int N=4000005;
int n,m,nxt[N],to[N],head[N],ecnt,pre[N],Nxt[N],a[N],ans;
bool vis[N],t[N];
void add(int bg,int ed) {nxt[++ecnt]=head[bg];to[ecnt]=ed;head[bg]=ecnt;}
void del(int x) {
    int p=pre[x];
    Nxt[p]=Nxt[x];
    pre[Nxt[x]]=p;
}
void bfs(int x) {
    queue<int>q;
    q.push(x);
    while(!q.empty()) {
        a[ans]++;
        int u=q.front();q.pop();
        for(int i=head[u];i;i=nxt[i]) {
            t[to[i]]=1;
        }
        for(int i=Nxt[0];i<=n;i=Nxt[i]) if(!t[i]) del(i),q.push(i);
        for(int i=head[u];i;i=nxt[i]) {
            t[to[i]]=0;
        }
    }
}
int main() {
    scanf("%d%d",&n,&m);
    for(int i=1,aa,b;i<=m;i++) {
        scanf("%d%d",&aa,&b);
        add(aa,b);
        add(b,aa);
    }
    for(int i=0;i<=n;i++) pre[i+1]=i,Nxt[i]=i+1;
    for(int i=Nxt[0];i<=n;i=Nxt[0]) {
        del(i);ans++;bfs(i);
    }
    cout<<ans<<endl;
    sort(a+1,a+1+ans);
    for(int i=1;i<=ans;i++) printf("%d ",a[i]);
    return 0;
}