【思路要点】
- 离线询问,为每一条边找到一个删除时间。
- 将过程倒过来,按照删除时间倒序加入每一条边。
- 我们将加入的边分为两类,加入后连接两个不同的联通块的称为树边,剩余的边称为非树边。
- 显然,树边的加入不会产生新的双连通分量,因此,我们可以预先将所有的树边加入图中,并处理出形成的森林中每个节点的深度等信息。
- 之后,我们每加入一条非树边,就会将森林中一条树链上所有的节点合并起来,可以通过并查集实现。
- 用 m a p map map 配合启发式合并维护颜色集合,时间复杂度 O ( N L o g 2 N + M + Q ) O(NLog^2N+M+Q) O(NLog2N+M+Q) 。
- 也可以用线段树合并维护颜色集合,时间复杂度 O ( N L o g N + M + Q ) O(NLogN+M+Q) O(NLogN+M+Q) 。
【代码】
#include<bits/stdc++.h> using namespace std; const int MAXN = 3e5 + 5; const int MAXM = 5e5 + 5; typedef long long ll; template <typename T> void chkmax(T &x, T y) {x = max(x, y); } template <typename T> void chkmin(T &x, T y) {x = min(x, y); } template <typename T> void read(T &x) { x = 0; int f = 1; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == '-') f = -f; for (; isdigit(c); c = getchar()) x = x * 10 + c - '0'; x *= f; } template <typename T> void write(T x) { if (x < 0) x = -x, putchar('-'); if (x > 9) write(x / 10); putchar(x % 10 + '0'); } template <typename T> void writeln(T x) { write(x); puts(""); } map <int, int> col[MAXN]; vector <int> a[MAXN], adj[MAXN], qry[MAXM]; ll ans[MAXM], curr; int n, m, k, q, num[MAXN], father[MAXN], depth[MAXN], f[MAXN]; int x[MAXM], y[MAXM], t[MAXM], p[MAXM], timer; int F(int x) { if (f[x] == x) return x; else return f[x] = F(f[x]); } void dfs(int pos, int fa) { f[pos] = pos; father[pos] = fa; depth[pos] = depth[fa] + 1; col[pos][num[pos]] = 1; for (auto x : a[pos]) if (x != fa) dfs(x, pos); } ll func(int x) { return x * (x - 1ll) / 2; } void merge(int x, int y) { x = F(x), y = F(y); int tx = x, ty = y; while (tx != ty) { if (depth[tx] > depth[ty]) tx = F(father[tx]); else ty = F(father[ty]); } int lca = tx; while (x != lca) { if (col[x].size() > col[lca].size()) swap(col[x], col[lca]); for (auto val : col[x]) { curr -= func(val.second); curr -= func(col[lca][val.first]); col[lca][val.first] += val.second; curr += func(col[lca][val.first]); } f[x] = F(father[x]); x = F(x); } x = y; while (x != lca) { if (col[x].size() > col[lca].size()) swap(col[x], col[lca]); for (auto val : col[x]) { curr -= func(val.second); curr -= func(col[lca][val.first]); col[lca][val.first] += val.second; curr += func(col[lca][val.first]); } f[x] = F(father[x]); x = F(x); } } int main() { read(n), read(m), read(k), read(q); for (int i = 1; i <= n; i++) read(num[i]); for (int i = 1; i <= m; i++) { read(x[i]), read(y[i]); adj[x[i]].push_back(i); adj[y[i]].push_back(i); } static bool flg[MAXM]; qry[1].push_back(0); for (int i = 1; i <= q; i++) { int x; read(x); if (!flg[x]) { flg[x] = true; for (auto y : adj[x]) if (t[y] == 0) t[y] = ++timer; } qry[timer + 1].push_back(i); } for (int i = 1; i <= m; i++) if (t[i] == 0) t[i] = ++timer; for (int i = 1; i <= m; i++) p[t[i]] = i; for (int i = 1; i <= n; i++) f[i] = i; memset(flg, false, sizeof(flg)); for (int i = m; i >= 1; i--) { int now = p[i]; if (F(x[now]) != F(y[now])) { flg[now] = true; a[x[now]].push_back(y[now]); a[y[now]].push_back(x[now]); f[F(x[now])] = F(y[now]); } } for (int i = 1; i <= n; i++) if (depth[i] == 0) dfs(i, 0); for (int i = m; i >= 1; i--) { int now = p[i]; if (!flg[now]) merge(x[now], y[now]); for (auto x : qry[i]) ans[x] = curr; } for (int i = 0; i <= q; i++) writeln(ans[i]); return 0; }