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

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