牛客提高组模拟4 快速访问——一道树剖“板子”题
link
为什么我会把树剖的常见 trick 忘了啊。。。
trick 1:lca -> x 向上跑 + y 向上跑
trick 2:深度可以用这个点到根点的个数表示
化简一下:\(ans=(d_i+d_j-2d_{LCA(i,j)})^2=d_i^2+d_j^2+4d^2_{LCA(i,j)}+2d_id_j-2d_id_{LCA(i,j)}-2d_jd_{LCA(i,j)}\)
考虑怎么修改/统计答案。第一项可以直接算,因为下标单调第二四项可以直接算。
第五六项实则是一个东西,可以树剖随便维护一下。
第四项可以对于它到根的点全部加上 \(d_x+d_x-1\) 来维护这个平方,其本质是 \(d_x^2-d_{x-1}^2\)。
然后就可以做了。
我考场上降智了想了对操作分块+换根dp+LCA 的 sb \(\mathcal {O}(n\sqrt {n/log_2(n)}log_2(n))\)。
好吧,考下才发现用 Tarjin 求 LCA 不会 MLE,每次用同一个数组即可,可以做到 \(<n\sqrt n \alpha(n)\)。。。算出来是 2e8,但因为递归常数太大分和暴力一样。。。
树剖代码有空再打吧,分块 code:
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <vector>
#define uint unsigned int
#define LL long long
using namespace std;
const int MAXN = 2e5 + 5;
int n, k, pos[MAXN], L[MAXN], R[MAXN], tot, Fa[MAXN][20], vis[MAXN], t, q, d[MAXN];
LL dp[MAXN], g[MAXN], c[MAXN], ans[MAXN];
int Head[MAXN], Ver[MAXN << 1], Next[MAXN << 1], ggg;
bool f[MAXN];
vector <int> v[MAXN];
// wow.().
void add(int x, int y) { Ver[++ ggg] = y; Next[ggg] = Head[x]; Head[x] = ggg; }
void DFS(int x, int fa) {
for(int i = Head[x]; i; i = Next[i]) {
int y = Ver[i]; if(y == fa) continue;
Fa[y][0] = x; d[y] = d[x] + 1;
for(int j = 1; j <= q; j ++) Fa[y][j] = Fa[Fa[y][j - 1]][j - 1];
DFS(y, x);
}
}
int LCA(int x, int y) {
if(d[x] < d[y]) swap(x, y);
for(int i = q; i >= 0; i --) if(d[Fa[x][i]] >= d[y]) x = Fa[x][i];
if(x == y) return x;
for(int i = q; i >= 0; i --) if(Fa[x][i] != Fa[y][i]) x = Fa[x][i], y = Fa[y][i];
return Fa[x][0];
}
void dfs(int x, int fa) {
for(int i = Head[x]; i; i = Next[i]) {
int y = Ver[i]; if(y == fa) continue;
dfs(y, x);
g[x] += g[y] + c[y]; c[x] += c[y]; dp[x] += dp[y] + 2 * g[y] + c[y];
}
c[x] += f[x];
}
void dfs1(int x, int fa) {
for(int i = Head[x]; i; i = Next[i]) {
int y = Ver[i]; if(y == fa) continue;
dp[y] = dp[y] + (dp[x] - dp[y] - 2 * g[y] - c[y]) + 2 * (g[x] - g[y] - c[y]) + (c[x] - c[y]);
g[y] = g[y] + (g[x] - g[y] - c[y] + (c[x] - c[y]));
c[y] = c[y] + (c[x] - c[y]);
dfs1(y, x);
}
}
LL Query(int l, int r, int id) {
if(l > r) return 0;
LL res = 0;
// for(int i = l; i <= r; i ++) t = LCA(i, id), printf("|%d %d %d %d %d %d|", i, id, t, d[t], d[i], d[id]), res += (LL)(d[i] + d[id] - 2 * d[t]) * (d[i] + d[id] - 2 * d[t]);
// return res;
int t = 0;
if(pos[l] == pos[r]) {
if(l == L[pos[l]] && r == R[pos[r]]) return 0;
for(int i = l; i <= r; i ++) t = LCA(i, id), res += (LL)(d[i] + d[id] - 2 * d[t]) * (d[i] + d[id] - 2 * d[t]);
return res;
}
int u = pos[l], v = pos[r];
if(l != L[u]) {
for(int i = l; i <= R[u]; i ++) t = LCA(i, id), res += (LL)(d[i] + d[id] - 2 * d[t]) * (d[i] + d[id] - 2 * d[t]);
}
if(r != R[v]) {
for(int i = L[v]; i <= r; i ++) t = LCA(i, id), res += (LL)(d[i] + d[id] - 2 * d[t]) * (d[i] + d[id] - 2 * d[t]);
}
return res;
}
int main() {
// freopen("large_2.in", "r", stdin);
// freopen("large_2.ans", "w", stdout);
int x, y; d[1] = 1;
scanf("%d%d", &n, &k); t = sqrt(n); q = log2(n) + 1;
for(int i = 1; i <= n; i ++) {
scanf("%d%d", &x, &y); x ++; y ++; add(x, y); add(y, x);
}
DFS(1, -1);
for(int i = 1; ; i ++) {
tot ++; L[tot] = R[tot - 1] + 1; R[tot] = min(n, L[tot] + t);
if(R[tot] == n) break;
}
for(int i = 1; i <= tot; i ++) L[i] ++, R[i] ++;
for(int i = 1; i <= tot; i ++) for(int j = L[i]; j <= R[i]; j ++) pos[j] = i;
for(int i = 1; i <= tot; i ++) {
for(int j = 1; j <= n + 1; j ++) f[j] = 0, g[j] = c[j] = dp[j] = 0;
for(int j = L[i]; j <= R[i]; j ++) f[j] = 1;
dfs(1, -1); dfs1(1, -1);
for(int j = R[i] + 1; j <= min(L[i] + k, n + 1); j ++) ans[j] += dp[j];
}
// memset(ans, 0, sizeof(ans));
for(int i = 2; i <= n + 1; i ++) {
int l = max(1, i - k), r = i - 1, u = LCA(1, i);
if(l == 1) l ++;
printf("%lld\n", ans[i] + Query(l, r, i) + (LL)(d[i] + d[u] - 2 * d[u]) * (d[i] + d[u] - 2 * d[u]));
}
return 0;
}