1. 程式人生 > >【題解】BZOJ 4458 GTY的OJ

【題解】BZOJ 4458 GTY的OJ

傳送門

Description

給定一棵 n 個結點的樹,每個結點有一個點權 ai。選出 m 條不重複的路徑,滿足路徑上的結點數在 [l,r] 範圍內。求點權和的最大值。

Solution

這道題其實就是 超級鋼琴 的樹上版本,我的題解在這裡 。建議先解決鏈上版本再拓展到樹上。即使超級鋼琴A掉的同學也建議看一下,真的不是為了增大訪問量,因為接下來的講述是基於那篇題解的。

在題解中,我用了一個1個三元組來表示狀態,類似的,這裡我也用一個三元組 (o,l,r) 表示以 o 為下端點,上端點(o 的祖先)與 o

的距離是 [l,r] 的最大點權和。因為是在樹上做區間裂解問題,所以不能像鏈上用下標直接做,而是要借用倍增。

主要的框架和那篇題解是一樣的,這裡就不再贅述了(是真的找不到什麼思想有明顯不同的地方),具體就看程式碼吧,有不明白的地方回到之前的題解,在那裡講的比較詳細

Code

#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; }