[bzoj1015][JSOI2008]星球大战——并查集+离线处理

题解

给定一张图,支持删点和询问连通块个数

按操作顺序处理的话要在删除点的同时维护图的形态(即图具体的连边情况),这是几乎不可做的

我们发现,这道题可以先读入操作,把没删的点的边先连上,然后再倒序处理操作

这样的话从删点变成了加点,而且只要维护连通块的数量,用并查集可以快速的解决这个问题

代码

#include <bits/stdc++.h>
using namespace std;
const int maxn = 4 * 200000;
int n, m, q[maxn];
vector<int> G[maxn];
int fa[maxn], added[maxn], des[maxn], ans[maxn];
int tot = 0;
int find(int x) { return fa[x] = fa[x] == x ? x : find(fa[x]); }
void add(int x) {
  int p = find(x), q;
  added[x] = 1;
  for (int i = 0; i < G[x].size(); i++) {
    if (added[G[x][i]]) {
      q = find(G[x][i]);
      if (p != q) {
        fa[q] = p;
        tot--;                //连通块少一个
      }
    }
  }
}
int main() {
  // freopen("input", "r", stdin);
  scanf("%d %d", &n, &m);
  for (int i = 1; i <= m; i++) {
    int x, y;
    scanf("%d %d", &x, &y);
    G[x].push_back(y);
    G[y].push_back(x);
  }
  memset(added, 0, sizeof(added));
  for (int i = 0; i < n; i++)
    fa[i] = i;
  int d;
  scanf("%d", &d);
  for (int i = 1; i <= d; i++) {
    scanf("%d", &q[i]);
    des[q[i]] = 1;
  }
  for (int i = 0; i < n; i++) {
    if (!des[i]) {
      tot++;
      add(i);
      added[i] = 1;
    }
  }
  ans[d + 1] = tot;
  for (int i = d; i > 0; i--) {
    tot++;                //连通块多一个
    add(q[i]);
    added[q[i]] = 1;
    ans[i] = tot;
  }
  for (int i = 1; i <= d + 1; i++)
    printf("%d
", ans[i]);
  return 0;
}
原文地址:https://www.cnblogs.com/gengchen/p/6433863.html