【題解】BZOJ 4458 GTY的OJ
阿新 • • 發佈:2018-12-10
給定一棵 個結點的樹,每個結點有一個點權 。選出 條不重複的路徑,滿足路徑上的結點數在 範圍內。求點權和的最大值。
這道題其實就是 超級鋼琴 的樹上版本,我的題解在這裡 。建議先解決鏈上版本再拓展到樹上。即使超級鋼琴A掉的同學也建議看一下,真的不是為了增大訪問量,因為接下來的講述是基於那篇題解的。
在題解中,我用了一個1個三元組來表示狀態,類似的,這裡我也用一個三元組 表示以 為下端點,上端點( 的祖先)與 的距離是 的最大點權和。因為是在樹上做區間裂解問題,所以不能像鏈上用下標直接做,而是要借用倍增。
主要的框架和那篇題解是一樣的,這裡就不再贅述了(是真的找不到什麼思想有明顯不同的地方),具體就看程式碼吧,有不明白的地方回到之前的題解,在那裡講的比較詳細
#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;
}