洛谷P1197 [JSOI2008]星球大战
由于题目不要求强制在线,所以可以离线。
而离线的话就会带来许多便利,所以我们可以先处理出全部打击后的图,通过并查集来判断是否连通。
然后再从后往前枚举,得出答案
#include <bits/stdc++.h>
using namespace std;
#define int long long
int n, m, cnt, k, data[400100], lin[400100], fa[400100], ha[400100];
struct edg {
int from, to, nex;
}e[601000];
inline void add(int f, int t)
{
e[++cnt].to = t;
e[cnt].nex = lin[f];
lin[f] = cnt;
e[cnt].from = f;
}
int find(int a)
{
if (fa[a] == a) return a;
return fa[a] = find(fa[a]);
}
inline void uoionn(int a, int b)
{
int ha = find(a), hb = find(b);
if (ha != hb)
fa[ha] = hb;
}
int ans[401000];
signed main()
{
scanf("%lld%lld", &n, &m);
for (int i = 0; i <= n; i++)
fa[i] = i;
for (int i = 1; i <= m; i++)
{
int x, y;
scanf("%lld%lld", &x, &y);
add(x, y);
add(y, x);
}
scanf("%lld", &k);
for (int i = 1; i <= k; i++)
scanf("%lld", &ha[i]), data[ha[i]] = 1;
int comb = n - k;//comb表示连通块个数,一开始只有n-k个,因为每个没有被打击的点,都是一个连通块
for (int i = 1; i <= 2 * m; i++)
{
if (find (e[i].from) != find (e[i].to) && !data[e[i].from] && !data[e[i].to])//如果这两个点没有相连,且没被摧毁,相当于最小生成树,将其相连
{
uoionn(e[i].from, e[i].to);
comb--;
}
}
ans[k + 1] = comb;
for (int i = k; i >= 1; i--)//逐一复原
{
data[ha[i]] = 0;
comb++;//修复
for (int j = lin[ha[i]]; j; j = e[j].nex)
{
int from = e[j].from, to = e[j].to;
if (!data[to] && find(from) != find(to))//如果没有相连且没被摧毁,就相连
{
comb--;
uoionn(from, to);
}
}
ans[i] = comb;
}
for (int i = 1; i <= k + 1; i++)
printf("%lld\n", ans[i]);
}