我们可以运用逆向思维,将破坏转化为修复。利用并查集判断联通。
#include <iostream>
using namespace std;
const int maxn = 4e5 + 10;
struct node {
int from, to, next;
}an[maxn]; //边
int n, m, k, cnt = 1;
int head[maxn]; //链头
int ans[maxn]; //输出答案
int broken[maxn];
int par[maxn];
int vis[maxn]; //是否被破坏
//链式前向星存图
void addedgs(int a, int b)
{
an[cnt].from = a;
an[cnt].to = b;
an[cnt].next = head[a];
head[a] = cnt;
cnt++;
}
//找根
int find(int x)
{
return par[x] == x ? x : par[x] = find(par[x]);
}
//合并
void unite(int a, int b)
{
a = find(a);
b = find(b);
if (a == b) return;
par[a] = b;
return;
}
int main(void)
{
ios::sync_with_stdio(false);
cin >> n >> m;
for (int i = 1; i <= n; i++) par[i] = i; //初始化并查集
for (int i = 0; i < m; i++) {
int a, b;
cin >> a >> b;
addedgs(a, b); //无向图
addedgs(b, a);
}
cin >> k;
int tot = n - k; //都没修复
for (int i = 1; i <= k; i++) {
cin >> broken[i];
vis[broken[i]] = 1;
}
for (int i = 1; i <= 2 * m; i++) {
//没被破坏且不同根,合并,联通量减1
if (!vis[an[i].from] && !vis[an[i].to] && find(an[i].from) != find(an[i].to)) {
unite(an[i].from, an[i].to);
tot--;
}
}
ans[k + 1] = tot;
//开始逆向修复
for (int i = k; i >= 1; i--) {
tot++; //被修复后,加1个单独的联通量
vis[broken[i]] = 0; //被修复
for (int j = head[broken[i]]; j; j = an[j].next) {
if (!vis[an[j].to] && find(an[j].from) != find(an[j].to)) {
unite(an[j].from, an[j].to);
tot--;
}
}
ans[i] = tot;
}
for (int i = 1; i <= k + 1; i++) cout << ans[i] << endl;
return 0;
}