2 条题解
-
0
LCA的贡献 题解
题目大意
个节点的树(1 为根),节点 有权值 。数组 是 的排列。对每个区间 ,若 这些节点的 LCA 是 ,则该区间贡献 。求所有区间贡献之和。
算法思路
转化为:对每个节点 ,统计"LCA 恰好为 "的区间个数,再乘 求和。
用DSU on tree处理。对每个节点,维护其子树内节点在数组 中出现的所有位置形成的若干连续段:
- 设 为 子树内节点在 中的位置集合。区间 的 LCA 为 ,当且仅当 内的位置都属于 ,且不全部落在 的某一个儿子的 内。
- 因为只关心每一个连续段的左右端点,直接维护个线段的左右端点,每加入一个位置,若它与左右已有的连续段相接则合并,并用 的增量更新"LCA 恰好为 "的区间数。
先对重儿子保留结果,轻儿子暴力加/删,整体复杂度 。
复杂度
。
参考代码
#include <bits/stdc++.h> using namespace std; using ll = long long; const int MAXN = 200010; int n; vector<int> g[MAXN]; int pos[MAXN]; // pos[node] = node 在数组 a 中的位置 ll w[MAXN]; int sz[MAXN], heavy[MAXN]; int segL[MAXN], segR[MAXN], mark[MAXN]; int ver[MAXN], curVer = 0; ll cnt[MAXN]; ll total = 0; ll tri(ll x) { return x * (x + 1) / 2; } void addPos(int p, int u) { mark[p] = curVer; segL[p] = segR[p] = p; if (mark[p + 1] != curVer) segL[p + 1] = segR[p + 1] = 0; if (mark[p - 1] != curVer) segL[p - 1] = segR[p - 1] = 0; ll a = 0, b = 0; if (segL[p - 1] && segR[p + 1]) { a = p - segL[p - 1]; b = segR[p + 1] - p; segR[segL[p - 1]] = segR[p + 1]; segL[segR[p + 1]] = segL[p - 1]; } else if (segL[p - 1]) { a = p - segL[p - 1]; segR[segL[p - 1]] = p; segL[p] = segL[p - 1]; } else if (segR[p + 1]) { b = segR[p + 1] - p; segL[segR[p + 1]] = p; segR[p] = segR[p + 1]; } cnt[u] = cnt[u] - tri(a) - tri(b) + tri(a + b + 1); } void dfsSize(int u) { sz[u] = 1; for (int v : g[u]) { dfsSize(v); sz[u] += sz[v]; if (sz[v] > sz[heavy[u]]) heavy[u] = v; } } void dfsAdd(int u, int target) { addPos(pos[u], target); for (int v : g[u]) dfsAdd(v, target); } void dfsClear(int u) { mark[pos[u]] = 0; segL[pos[u]] = segR[pos[u]] = 0; for (int v : g[u]) dfsClear(v); } void dfsDsu(int u) { ver[pos[u]] = ++curVer; for (int v : g[u]) { if (v == heavy[u]) continue; dfsDsu(v); dfsClear(v); } if (heavy[u]) { dfsDsu(heavy[u]); cnt[u] = cnt[heavy[u]]; curVer = ver[pos[u]] = ver[pos[heavy[u]]]; for (int v : g[u]) { if (v != heavy[u]) dfsAdd(v, u); } } curVer = ver[pos[u]]; addPos(pos[u], u); } void dfsAnswer(int u) { ll k = cnt[u]; for (int v : g[u]) { k -= cnt[v]; dfsAnswer(v); } total += k * w[u]; } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); cin >> n; for (int i = 2; i <= n; ++i) { int p; cin >> p; g[p].push_back(i); } for (int i = 1; i <= n; ++i) { int x; cin >> x; pos[x] = i; } for (int i = 1; i <= n; ++i) cin >> w[i]; dfsSize(1); dfsDsu(1); dfsAnswer(1); cout << total << '\n'; return 0; }
- 1
信息
- ID
- 103
- 时间
- 1000ms
- 内存
- 512MiB
- 难度
- 9
- 标签
- (无)
- 递交数
- 24
- 已通过
- 4
- 上传者