1098 办公楼biu

题意:

给出一个图,求其补图的连通块个数与大小;

原图点数n<=100000,m<=2000000;

题解:

这道题主要是对于复杂度的优化分析上吧;

一开始有个显然的O(n^2*α(n))暴力做法,枚举每一条补图中的边然后并查集啦;

这样空间与时间都是无法接受的;

考虑另一种做法:每次枚举一个未在连通块的点,然后从它开始宽搜出它所在的连通块;

具体是枚举它的所有原图的边,标记起来,枚举边之后再枚举所有的点,将未标记的点加入该连通块,并加入队列继续宽搜;

为了节约无用的枚举,我们还需要对所有点构建链表,将已经在某个块内的点删除;

这个算法的复杂度是O(n+m)的;

原因是每一个点仅进行了一次宽搜的拓展;

并且在每次拓展中,枚举边表总复杂度是O(m);

而之后的枚举剩下的点,我们将点分为两部分:已标记的点的复杂度计在O(m)之内,而未标记的点将会被加入队列,这个过程对每个点也仅有一次;

证明了复杂度就结束了,可以放心大胆的去*这道题啦;

代码:

#include<queue>#include<stdio.h>#include<string.h>#include<algorithm>#define N 110000#define M 2100000using namespace std;int enext[M<<1],to[M<<1],head[N],ce;int st[N],last[N],next[N],top;bool vis[N],cov[N];queue<int>q;void add(int x,int y){to[++ce]=y;enext[ce]=head[x];head[x]=ce;}void del(int x){next[last[x]]=next[x];last[next[x]]=last[x];}int main(){int n,m,i,j,k,x,y,ans;scanf("%d%d",&n,&m);for(i=1;i<=m;i++){scanf("%d%d",&x,&y);add(x,y),add(y,x);}for(i=1;i<n;i++)next[i]=i+1,last[i+1]=i;next[0]=1;for(i=1;i<=n;i++){if(!vis[i]){st[++top]=1;vis[i]=1;q.push(i);del(i);while(!q.empty()){x=q.front(),q.pop();for(j=head[x];j;j=enext[j]){if(!vis[to[j]]){cov[to[j]]=1;}}for(j=next[0];j;j=next[j]){if(!cov[j]){vis[j]=1;st[top]++;del(j);q.push(j);}elsecov[j]=0;}}}}sort(st+1,st+top+1);printf("%d\n",top);for(i=1;i<=top;i++)printf("%d ",st[i]);return 0;}

,我想一个人旅行,背上简单的行囊,踏上行程,

1098 办公楼biu

相关文章:

你感兴趣的文章:

标签云: