题意
给定有 n n n个节点的树,节点 i i i与 ⌊ i 2 ⌋ \lfloor\frac i2\rfloor ⌊2i⌋有距离为 L i L_i Li的边。
m m m次询问,每次询问给出 A , H A,H A,H
若某个节点到 A A A的距离 L L L小于 H H H,则会产生 H − L H-L H−L的贡献,求树上所有点与 A A A产生的贡献之和。
n ≤ 1 0 6 , m ≤ 1 0 5 n\le10^6,m\le10^5 n≤106,m≤105
题解
这棵树是一颗完全二叉树.设 d e p u {\rm dep}_u depu表示 u u u到 1 1 1的距离。
分别考虑 u u u子树内的答案和子树外的答案:
- 子树内的答案即
∑ d e p v − d e p u < H H − ( d e p v − d e p u ) = ∑ d e p v < H + d e p u ( H + d e p u ) − d e p v = s ( H + d e p u ) − ∑ d e p v < H + d e p u d e p v \sum_{{\rm dep}_v-{\rm dep}_u<H}H-({\rm dep}_v-{\rm dep}_u)=\sum_{{\rm dep}_v<H+{\rm dep}_u}(H+{\rm dep}_u)-{\rm dep}_v=s(H+{\rm dep}_u)-\sum_{{\rm dep}_v<H+{\rm dep}_u}{\rm dep}_v depv−depu<H∑H−(depv−depu)=depv<H+depu∑(H+depu)−depv=s(H+depu)−depv<H+depu∑depv
其中 s = ∑ [ d e p v < H + d e p u ] s=\sum[{\rm dep}_v<H+{\rm dep}_u] s=∑[depv<H+depu].
将 u u u子树内所有的 d e p v {\rm dep}_v depv放到的一个数组 a u a_u au中,对 a u a_u au排序后求其前缀和数组 S u m u {\rm Sum}_u Sumu。
那么这部分答案就可以通过对 a u a_u au数组二分得到 s s s算出来,即 s ( H + d e p u ) − S u m u [ s ] s(H+{\rm dep}_u)-{\rm Sum}_u[s] s(H+depu)−Sumu[s]。 - 父亲
f
a
u
fa_u
fau及其兄弟节点
w
w
w.父亲的答案即
H
−
L
u
H-L_u
H−Lu.兄弟节点
w
w
w的子树内的节点的答案即
∑ d e p v − d e p w + L u + L w < H H − ( d e p v − d e p w + L u + L w ) = s ′ ( H + d e p w − L u − L w ) − ∑ d e p v < H + d e p w − L u − L w d e p v \sum_{{\rm dep}_v-{\rm dep}_w+L_u+L_w<H}H-({\rm dep}_v-{\rm dep}_w+L_u+L_w)=s'(H+{\rm dep}_w-L_u-L_w)-\sum_{{\rm dep}_v<H+{\rm dep}_w-L_u-L_w}{\rm dep}_v depv−depw+Lu+Lw<H∑H−(depv−depw+Lu+Lw)=s′(H+depw−Lu−Lw)−depv<H+depw−Lu−Lw∑depv
可以发现上式和 1. 1. 1.中的式子形式相同,故可以统一处理. - 父亲的父亲及父亲的兄弟节点…
由于这是棵完全二叉树,故暴力跳父亲算答案最多跳
log
n
\log n
logn次,算上二分的
log
n
\log n
logn,单次询问复杂度为
log
2
n
\log^2n
log2n.
a
u
a_u
au可以自底向上通过归并求出,且根据完全二叉树的特性,可以采用非递归的形式求解降低常数。
时间复杂度 O ( n log n + m log 2 n ) O(n\log n+m\log^2n) O(nlogn+mlog2n)
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 5;
typedef long long ll;
int n, m, dep[N], Len[N];
vector<int> a[N];
vector<ll> Sum[N];
inline ll Calc(int u, int H) {
if (H <= 0 || !a[u].size())
return 0;
int s = lower_bound(a[u].begin(), a[u].end(), H) - a[u].begin();
return s ? (ll)s * H - Sum[u][s - 1] : 0;
}
int main() {
scanf("%d%d", &n, &m);
for (int i = 2; i <= n; ++i)
scanf("%d", Len + i);
for (int u = 1; u <= n / 2; ++u) {
int L = u << 1, R = L | 1;
if (L <= n)
dep[L] = dep[u] + Len[L];
if (R <= n)
dep[R] = dep[u] + Len[R];
}
for (int u = n; u; --u) {
int L = u << 1, R = L | 1;
a[u].emplace_back(dep[u]);
if (R <= n)
merge(a[L].begin(), a[L].end(), a[R].begin(), a[R].end(),
back_inserter(a[u]));
else if (L <= n)
a[u].insert(a[u].end(), a[L].begin(), a[L].end());
Sum[u].resize(a[u].size());
Sum[u][0] = a[u][0];
for (int i = 1; i < (int)Sum[u].size(); ++i)
Sum[u][i] = Sum[u][i - 1] + a[u][i];
}
for (int u, H; m--;) {
scanf("%d%d", &u, &H);
ll Ans = Calc(u, H + dep[u]);
for (H -= Len[u]; H > 0 && u != 1; H -= Len[u >>= 1])
Ans += H + Calc(u ^ 1, H - Len[u ^ 1] + dep[u ^ 1]);
printf("%lld\n", Ans);
}
return 0;
}