BZOJ 1098: [POI2007]辦公樓biu 並查集優化bfs找反圖聯通塊

時空隧道


分析:
如果數據範圍改成n<=1000那就是一道水題了…
題目翻譯過來就是一張無向圖中沒有邊直接相連的兩個點一定在一個聯通塊中,問最多有多少個聯通塊…所以我們暴力建一張反圖用並查集求出聯通塊個數就好了…
但是那就難在了n<=100000…雖說n方過百萬也不是沒有過…但是那也是特殊題目特殊方法啊(其實就是數據水~)…
所以考慮正解…我們考慮如果兩個點在反圖中有邊,那麼這兩個點在原圖中一定沒有邊…所以我們枚舉每一個點,從這個點出發bfs把沒有訪問過的點入隊(這些點在反圖中和枚舉的點是同一個聯通塊),然後再從隊列中取出這些點繼續bfs找點…
這不還是n^2的嗎…QAQ
我們考慮如果兩個點在一個聯通塊中,那麼這兩個點中有一個點是沒有存在的必要的…所以我們在找聯通塊的時候把找到的點去掉就好了…
怎麼去??方法很多…可以維護一個鏈表…當然機智的方法是並查集(NicoNicoNicoNi~~)…


代碼如下:

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>
//by NeighThorn
using namespace std;
const int maxn=100000+5,maxm=2000000+5;
int n,m,fa[maxn],hd[maxn],to[maxm*2],nxt[maxm*2],cnt,vis[maxn],ans,num[maxn];
inline void add(int x,int y){
    to[cnt]=y;nxt[cnt]=hd[x];hd[x]=cnt++;
}
inline int find(int x){
    return fa[x]==x?x:fa[x]=find(fa[x]);
}
inline void bfs(int root){
    queue<int> q;q.push(root),num[++ans]=1;
    while(!q.empty()){
        int top=q.front();q.pop();fa[top]=find(top+1);
        for(int i=hd[top];i!=-1;i=nxt[i])
            vis[to[i]]=top;
        for(int i=find(1);i<=n;i=find(i+1))
            if(vis[i]!=top)
                num[ans]++,fa[i]=find(i+1),q.push(i) ;
    }
}
signed main(void){
    memset(hd,-1,sizeof(hd));
    scanf("%d%d",&n,&m);cnt=0;
    for(int i=1,x,y;i<=m;i++)
        scanf("%d%d",&x,&y),add(x,y),add(y,x);
    for(int i=1;i<=n+1;i++)
        fa[i]=i;
    for(int i=1;i<=n;i=find(i+1))
        bfs(i);
    cout<<ans<<endl;sort(num+1,num+ans+1);
    for(int i=1;i<=ans;i++)
        printf("%d ",num[i]);
    puts("");
    return 0;
}

by >_< NeighThorn

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章