ECC 剖?

RT,动态加边,会形成 ECC,但是为了不改变树的形态,选择将 ECC 中的点划在一起,同时指向深度最低的点,用并查集,由于按深度合并,所以是 nlogn,如果加一条边,就从两点向上找 LCA ,将路径中的所有点都合并,过程中维护答案。

code

#include <iostream>
#include <vector>
#define int long long

using namespace std;

const int MaxN = 1e5 + 10;

int f[MaxN], gfa[MaxN], p[MaxN], sz[MaxN], szz[2][MaxN], d[MaxN], ans[MaxN], n, q;
vector<int> g[MaxN];

void DFS(int x = 1, int fa = 0) {
  sz[x] = 1, p[x] = x, f[x] = -1;
  for (int i : g[x]) {
    if (i == fa) continue;
    d[i] = d[x] + 1, gfa[i] = x, DFS(i, x), sz[x] += sz[i], szz[0][x] += sz[i], szz[1][x] += sz[i] * sz[i];
  }
  if (x == 1) ans[x] = szz[0][x] * szz[0][x] - szz[1][x];
  else ans[x] = (szz[0][x] + n - sz[x]) * (szz[0][x] + n - sz[x]) - szz[1][x] - (n - sz[x]) * (n - sz[x]);
  ans[0] += ans[x];
}

int ff(int x) {
  return f[x] < 0 ? x : f[x] = ff(f[x]);
}

void merge(int x, int y) {
  x = ff(x), y = ff(y);
  if (x == y) return;
  if (d[x] > d[y]) swap(x, y);
  f[x] += f[y], f[y] = x;
}

void LCA(int x, int y, int cnt1 = 0, int cnt2 = 0) {
  x = ff(x), y = ff(y);
  while (ff(x) != ff(y)) {
    if (d[ff(x)] > d[ff(y)]) swap(x, y);
    cnt1 += szz[0][y] - sz[y];
    cnt2 += szz[1][y] - sz[y] * sz[y];
    ans[0] -= ans[y];
    merge(gfa[y], y), y = ff(y);
  }
  int lca = d[x] > d[y] ? y : x;
  cnt1 += szz[0][lca], cnt2 += szz[1][lca];
  szz[0][lca] = cnt1, szz[1][lca] = cnt2, ans[0] -= ans[lca];
  int tmp = -f[lca];
  ans[lca] = (tmp * (tmp - 1) * (tmp - 2)) + 2 * tmp * (tmp - 1) * (n - tmp) + ((szz[0][lca] + n - sz[lca]) * (szz[0][lca] + n - sz[lca]) - szz[1][lca] - (n - sz[lca]) * (n - sz[lca])) * tmp;
  ans[0] += ans[lca];
}

signed main() {
  ios::sync_with_stdio(0), cin.tie(0);
  cin >> n;
  for (int i = 1, u, v; i < n; i++) {
    cin >> u >> v;
    g[u].push_back(v);
    g[v].push_back(u);
  }
  DFS();
  cout << ans[0] << '\n';
  cin >> q;
  for (int u, v, lca; q; q--) {
    cin >> u >> v, LCA(u, v);
    cout << ans[0] << '\n';
  }
  return 0;
}
posted @ 2025-08-15 14:50  yabnto  阅读(10)  评论(0)    收藏  举报