给一个图,每次删除一个点,求连通块数量。
链接
题解
记录所有被删除的点,将除这些点邻边之外所有的边加入并查集,将操作离线变为加边,每次加入一个点的一些邻边,当某个邻边指向的点已被加入时,加入这条边。
代码
#include <cstdio>
#include <vector>
const int MAXM = 200000;
const int MAXN = 2 * MAXM;
struct UnionFindSet {
int a[MAXN], rk[MAXN];
void init(const int n) { for (int i = 0; i < n; i++) a[i] = i, rk[i] = 1; }
int find(const int x) { return x == a[x] ? x : a[x] = find(a[x]); }
bool test(const int x, const int y) { return find(x) == find(y); }
void merge(const int x, const int y) {
const int p = find(x), q = find(y);
if (p == q) return;
if (rk[p] > rk[q]) a[q] = p;
else if (rk[q] > rk[p]) a[p] = q;
else a[p] = q, rk[q]++;
}
} ufs;
struct Edge {
bool added;
int u, v;
} E[MAXM];
struct Node {
std::vector<Edge *> e;
bool added;
bool willDelete;
} N[MAXN];
inline void addAllEdge(Node *v, int &ans) {
ans++;
for (std::vector<Edge *>::const_iterator it = v->e.begin(); it != v->e.end(); it++) {
Edge *const &e = *it;
if (!e->added && N[e->u].added && N[e->v].added && !ufs.test(e->u, e->v)) {
ufs.merge(e->u, e->v);
// printf("merge(%d, %d)\n", e->u, e->v);
ans--;
e->added = true;
}
}
}
int main() {
int n, m, k, ans;
static int a[MAXN];
scanf("%d %d", &n, &m), ans = 0;
ufs.init(n);
for (int i = 0; i < m; i++) scanf("%d %d", &E[i].u, &E[i].v), N[E[i].u].e.push_back(&E[i]), N[E[i].v].e.push_back(&E[i]);
scanf("%d", &k);
for (int i = k - 1; i >= 0; i--) scanf("%d", &a[i]), N[a[i]].willDelete = true;
for (int i = 0; i < n; i++) if (!N[i].willDelete) N[i].added = true, addAllEdge(&N[i], ans);
std::vector<int> anses;
anses.push_back(ans);
for (int i = 0; i < k; i++) {
// printf("before %d\n", a[i]);
Node *v = &N[a[i]];
v->added = true;
addAllEdge(v, ans);
anses.push_back(ans);
}
for (std::vector<int>::const_reverse_iterator it = anses.rbegin(); it != anses.rend(); it++) printf("%d\n", *it);
return 0;
}