BZOJ1098

</pre><p><span style="white-space:pre">															</span><span style="font-size:24px;">BZOJ1098</span></p><pre><span style="font-family:Microsoft YaHei;">题意:先转为补图,然后求联通块的个数。
</span><span style="font-family:Microsoft YaHei;">思路:
</span><span style="font-family:Microsoft YaHei;"><span style="white-space:pre">	</span>1.可以证明答案是O(N^1/2)级别的,因为首先对于一定的M,若答案最大则每个联通块必为点,即答案为M^1/2(假设N个点,每个点必然要和其它N-1个点连线,若N再增加,则每个点平均出度不足)。
</span><span style="font-family:Microsoft YaHei;"><span style="white-space:pre">	</span>2.直接转化为补图是不现实的,即一次性求出答案是不现实的,提示我们按一个一个加点的顺序做。
</span><span style="font-family:Microsoft YaHei;">解法:
</span><span style="font-family:Microsoft YaHei;"><span style="white-space:pre">	</span>使用并查集,对于每个点,统计和已经统计过的所有联通块内点的联通情况,若点i与联通块内某个点无连边,则i和该联通块合并。具体实现我用了循环队列。。。
</span><span style="font-family:Microsoft YaHei;"><span style="white-space:pre">	</span>另外,复杂度的话应该是O(N*α+N*M^1/2)</span>
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn = 100010, maxm = 4000010;
int m0 = 0, first[maxn], next[maxm], to[maxm], fa[maxn], cnt[maxn], vis[maxn];
int q[maxn], front=1, rear=2;
int n, m, Ans = 0, Anscnt[maxn], viscnt, vispos[maxn];
int getfa(int x)
{
	if (fa[x] != x)
		return fa[x] = getfa(fa[x]);
	else
		return x;
}
void addedge(int u, int v)
{
	m0++; next[m0] = first[u]; first[u] = m0; to[m0] = v;
}
void add(int &i)
{
	if (i == 100005)i = 1;
	else i++;
}
int main()
{
	int u, v;
	scanf("%d%d", &n, &m);
	for (int i = 1; i <= m; i++)
	{
		scanf("%d%d", &u, &v);
		addedge(u, v);
		addedge(v, u);
	}
	cnt[1] = 1;
	fa[1] = 1;
	q[1] = 1;
	int newrear;
	for (int i = 2; i <= n; i++)
	{
		cnt[i] = 1;
		fa[i] = i;
		viscnt = 0;
		for (int e = first[i]; e; e = next[e])
			if(fa[to[e]])
			{
				vispos[++viscnt] = getfa(to[e]);
				vis[vispos[viscnt]]++;
			}
		newrear = rear;
		for (int j = front; j != rear; add(j))
		{
			front = j;
			if (cnt[q[j]] != vis[q[j]])
			{
				cnt[i] += cnt[q[j]];
				cnt[q[j]] = 0;
				fa[q[j]] = i;
			}
			else
			{
				q[newrear] = q[j];
				add(newrear);
			}
		}
		add(front);
		q[newrear] = i;
		add(newrear);
		for (int j = 1; j <= viscnt; j++)
			vis[vispos[j]] = 0;
		rear = newrear;
	}
	for (int j = front; j != rear; add(j))
	{
		Ans++;
		Anscnt[Ans] = cnt[q[j]];
	}
	sort(Anscnt + 1,Anscnt + Ans + 1);
	printf("%d\n", Ans);
	for (int i = 1; i <= Ans; i++)
		printf("%d ", Anscnt[i]);
	return 0;
}

	


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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值