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;
}

浙公网安备 33010602011771号