bzoj-1098 办公楼biu

141 篇文章 0 订阅
75 篇文章 0 订阅

题意:

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

原图点数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 2100000
using 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);
					}
					else
						cov[j]=0;
				}
			}
		}
	}
	sort(st+1,st+top+1);
	printf("%d\n",top);
	for(i=1;i<=top;i++)
		printf("%d ",st[i]);
	return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值