給一棵樹(n ≤ 10⁶),m 次查詢 (m ≤ 5×10⁴),每次給 k 個關鍵點 (Σk ≤ 2n)。求這 k 點之間:
1. 兩兩距離和。
2. 最小距離。
3. 最大距離。
對每個節點 u 維護:
- cnt[u]:子樹內關鍵點數。
- sumDist[u]:子樹內關鍵點到 u 的距離和。
- maxD[u]:子樹內關鍵點到 u 的最大距離。
- minD[u]:子樹內關鍵點到 u 的最小距離(關鍵點為 0,非關鍵點初 INF)。
合併子節點 v 到 u(邊長 w):
- 距離和貢獻:sumDist[u]*cnt[v] + sumDist[v]*cnt[u] + cnt[u]*cnt[v]*w。
- 最小距離:min(minD[u], minD[v] + w)(需考慮跨子樹:minD[u] + minD[v] + w)。
- 最大距離:類似,取 max。
- 更新 cnt[u] += cnt[v], sumDist[u] += sumDist[v] + cnt[v]*w, maxD[u] = max(maxD[u], maxD[v]+w), minD[u] = min(minD[u], minD[v]+w)。
全局答案在合併時累加更新。
struct VTree {
vector adj[MAXN];
long long cnt[MAXN], sumD[MAXN], maxD[MAXN], minD[MAXN];
bool isKey[MAXN];
long long ansSum, ansMin, ansMax;
void dp(int u) {
cnt[u] = isKey[u];
sumD[u] = 0;
maxD[u] = isKey[u] ? 0 : -INF;
minD[u] = isKey[u] ? 0 : INF;
for (int v : adj[u]) {
int w = depth[v] - depth[u]; // 原樹距離
dp(v);
// 更新全局答案
ansSum += (sumD[u] + cnt[u]*w) * cnt[v] + sumD[v] * cnt[u];
ansMin = min(ansMin, minD[u] + minD[v] + w);
ansMax = max(ansMax, maxD[u] + maxD[v] + w);
// 合併
cnt[u] += cnt[v];
sumD[u] += sumD[v] + cnt[v]*w;
maxD[u] = max(maxD[u], maxD[v] + w);
minD[u] = min(minD[u], minD[v] + w);
}
}
};
這是虛樹 DP 的進階應用,同時維護多種聚合信息(和/最小/最大),展示了虛樹技術的強大通用性。