Description D e s c r i p t i o n
给定一棵 n n 个结点的树,每个结点有一个点权 。选出 m m 条不重复的路径,满足路径上的结点数在 范围内。求点权和的最大值。
Solution S o l u t i o n
这道题其实就是 超级钢琴 的树上版本,我的题解在这里 。建议先解决链上版本再拓展到树上。即使超级钢琴A掉的同学也建议看一下,真的不是为了增大访问量,因为接下来的讲述是基于那篇题解的。
在题解中,我用了一个1个三元组来表示状态,类似的,这里我也用一个三元组 (o,l,r) ( o , l , r ) 表示以 o o 为下端点,上端点( 的祖先)与 o o 的距离是 的最大点权和。因为是在树上做区间裂解问题,所以不能像链上用下标直接做,而是要借用倍增。
主要的框架和那篇题解是一样的,这里就不再赘述了(是真的找不到什么思想有明显不同的地方),具体就看代码吧,有不明白的地方回到之前的题解,在那里讲的比较详细
Code C o d e
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
#define MAXN 500005
#define LOG 20
#define max(x, y) ((x) > (y) ? (x) : (y))
long long sum[MAXN], depth[MAXN], ST[MAXN][LOG], anc[MAXN][LOG];
int calc(int x, int y) {
return sum[ anc[x][0] ] < sum[ anc[y][0] ] ? x : y;
}
void init(int n) {
for (int j = 1; j < LOG; j++)
for (int i = 1; i <= n; i++) {
anc[i][j] = anc[ anc[i][j - 1] ][j - 1];
if (depth[i] >= (1 << j))
ST[i][j] = calc(ST[i][j - 1], ST[ anc[i][j - 1] ][j - 1]);
}
}
int query(int x, int y) {
int ret = ST[x][0];
for (int i = LOG - 1; i >= 0; i--) {
if (depth[ anc[y][i] ] >= depth[x]) {
ret = calc(ret, ST[y][i]);
y = anc[y][i];
}
}
return ret;
}
int swim(int x, int H) { // x 向上走 H 的深度后的结点标号
for (int i = 0; H > 0; i++) {
if (H & 1)
x = anc[x][i];
H >>= 1;
}
return x;
}
struct element {
int o, l, r, t;
element() {}
element(int o, int l, int r) : o(o), l(l), r(r), t(query(l, r)) {}
friend bool operator < (const element& a, const element& b) {
return sum[a.o] - sum[ anc[a.t][0] ] < sum[b.o] - sum[ anc[b.t][0] ];
}
};
std::priority_queue< element > Q;
int main() {
int n, k, L, R;
scanf("%d", &n);
for (int i = 1; i <= n; i++)
scanf("%lld", &anc[i][0]);
for (int i = 1; i <= n; i++) {
scanf("%lld", &sum[i]);
sum[i] += sum[ anc[i][0] ];
depth[i] = depth[ anc[i][0] ] + 1;
ST[i][0] = i;
}
init(n); //倍增预处理
scanf("%d%d%d", &k, &L, &R);
for (int i = 1; i <= n; i++)
if (depth[i] >= L)
Q.push(element(i, max(1, swim(i, R - 1)), swim(i, L - 1)));
long long ans = 0;
while (k--) {
int o = Q.top().o, l = Q.top().l, r = Q.top().r, t = Q.top().t;
Q.pop();
ans += (long long)sum[o] - sum[ anc[t][0] ];
if (l != t) Q.push(element(o, l, anc[t][0])); //裂解
if (t != r) Q.push(element(o, swim(o, depth[o] - depth[t] - 1), r));
}
printf("%lld\n", ans);
return 0;
}