【题解】BZOJ 4458 GTY的OJ

传送门

Description D e s c r i p t i o n

给定一棵 n n 个结点的树,每个结点有一个点权 ai a i 。选出 m m 条不重复的路径,满足路径上的结点数在 [l,r] [ l , r ] 范围内。求点权和的最大值。

Solution S o l u t i o n

这道题其实就是 超级钢琴 的树上版本,我的题解在这里 。建议先解决链上版本再拓展到树上。即使超级钢琴A掉的同学也建议看一下,真的不是为了增大访问量,因为接下来的讲述是基于那篇题解的。

在题解中,我用了一个1个三元组来表示状态,类似的,这里我也用一个三元组 (o,l,r) ( o , l , r ) 表示以 o o 为下端点,上端点( o o 的祖先)与 o o 的距离是 [l,r] [ l , r ] 的最大点权和。因为是在树上做区间裂解问题,所以不能像链上用下标直接做,而是要借用倍增。

主要的框架和那篇题解是一样的,这里就不再赘述了(是真的找不到什么思想有明显不同的地方),具体就看代码吧,有不明白的地方回到之前的题解,在那里讲的比较详细

Code C o d e

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

你可能感兴趣的:(BZOJ,倍增,贪心)