洛谷 P1197 [JSOI2008]星球大战

题意简述

有n个点和m条通道,
现在按顺序破坏k个点
求每一次破坏后联通块的个数

题解思路

并查集,逆序做,
先假设给的k个星球全都被炸,求出此时的联通块个数,就是经过k次打击的联通块个数。
然后每次加上一个被炸的星球,就求出了经过每次次打击后的联通块个数

代码

#include <cstdio>
using namespace std;
int n, m, k, u, v, cnt, sum, x, y;
int d[401000], fa[401000], ans[401000];
int f[401000], to[801000], nxt[801000];
bool bd[401000];
void add_edge(int u, int v)
{
	to[++cnt] = v;
	nxt[cnt] = f[u];
	f[u] = cnt;
}
int gf(int x)
{
	return x == fa[x] ? x : fa[x] = gf(fa[x]);
}
int main()
{
	scanf("%d%d", &n, &m);
	for (register int i = 1; i <= m; ++i)
	{
		scanf("%d%d", &u, &v);
		add_edge(u, v);
		add_edge(v, u);
	}
	for (register int i = 0; i < n; ++i) fa[i] = i;
	scanf("%d", &k);
	for (register int i = 1; i <= k; ++i)
	{
		scanf("%d", &d[i]);
		bd[d[i]] = 1;
	}
	sum = n - k;
	for (register int i = 0; i < n; ++i)
		if (!bd[i])
			for (register int j = f[i]; j; j = nxt[j])
				if (!bd[to[j]])
				{
					x = gf(i);
					y = gf(to[j]);
					if (x != y)
					{
						fa[y] = x;
						--sum;
					}
				}
	ans[k + 1] = sum;
	for (register int i = k; i; --i)
	{
		++sum;
		bd[d[i]] = 0;
		x = gf(d[i]);
		for (register int j = f[d[i]]; j; j = nxt[j])
			if (!bd[to[j]])
			{
				y = gf(to[j]);
				if (x != y)
				{
					fa[y] = x;
					--sum;
				}
			}
		ans[i] = sum;
	}
	for (register int i = 1; i <= k + 1; ++i) printf("%d
", ans[i]);
}
原文地址:https://www.cnblogs.com/xuyixuan/p/9594419.html