[bzoj]1098: [POI2007]办公楼biu

原题链接:[POI2007]办公楼biu

题意

求一个稀疏图的补图的连通块个数以及每个连通块里的点数。$N,M$非常大。

分析

考虑广搜,枚举每个点,维护一个链表记录未被访问到的点。

每次访问到一个新点,删除所有与它不相连的点即可。

具体做法可以是标记所有与它相连的点,然后在链表里删除+入队其他点。

注意这里可以用邻接表储存,并且按序号排序,可以降低查询边的复杂度。

每次查询的时候从头开始遍历链表,同时一个指针遍历这个点的出边。

由于都是递增的,一遍$O(n)$就可以处理了。

然后计算一下时间:每个点只能被入队一次,删除之后就不会再被遍历了,而且在删除之前直接跳过,所以均摊O(n)。

代码

#include <bits/stdc++.h>
#define ll long long
#define ull unsigned long long
#define Mid ((l+r)/2)
#define lson (rt<<1)
#define rson (rt<<1|1)
using namespace std;
const int N=1000009;
int read(){
    char c;int num,f=1;
    while(c=getchar(),!isdigit(c))if(c=='-')f=-1;num=c-'0';
    while(c=getchar(), isdigit(c))num=num*10+c-'0';
    return f*num;
}
vector<int>g[N];queue<int>q;
int n,m,ans[N],cnt,del[N],f[N],nxt[N],pre[N];
void add(int u,int v){g[u].push_back(v);g[v].push_back(u);}
void bfs(int x){
    int now=0;cnt++;
    while(q.size())q.pop();q.push(x);
    while(q.size()){
        x=q.front();q.pop();
        for(int i=nxt[0],k=0;i<=n;i=nxt[i]){
            while(k<g[x].size()&&g[x][k]<i)k++;
            if(g[x][k]==i||del[i])continue;
            del[i]=1;nxt[pre[i]]=nxt[i];pre[nxt[i]]=pre[i];
            q.push(i);now++;
        }
    }
    ans[cnt]=now;
}
int main()
{
    n=read();m=read();
    for(int i=1;i<=m;i++)add(read(),read());
    for(int i=1;i<=n;i++)sort(g[i].begin(),g[i].end());
    for(int i=1;i<=n;i++)nxt[i]=i+1,pre[i]=i-1;nxt[0]=1;pre[n+1]=n;
    for(int i=1;i<=n;i++)if(!del[i])bfs(i);
    printf("%d\n",cnt);sort(ans+1,ans+1+cnt);
    for(int i=1;i<=cnt;i++)printf("%d ",ans[i]);printf("\n");
    return 0;
} 
View Code

 

posted @ 2019-08-06 10:50  _onglu  阅读(166)  评论(0编辑  收藏  举报