• BZOJ 2006 [NOI2010] 超级钢琴


    DescriptionDescription
    有 nn 个音符,编号为 11 至 nn 。第 ii 个音符的美妙度为 AiAi 。

    我们要找到 kk 段超级和弦组成的乐曲,每段连续的音符的个数 xx 满足 L≤x≤RL≤x≤R ,求乐曲美妙度的最大值。

    SolutionSolution
    贪心 + 堆 + RMQ

    首先可以看到,每段超级和弦都是连续的,美妙度是这段区间内所有美妙度的和。可以想到,每次求解区间和显然是不合算的,所以考虑到用前缀和。

    考虑暴力,我们需要把所有满足条件的字段抽出来排个序,但这实在是不可想象。所以考虑使用贪心思想来解决这个问题。

    先想预处理。我们定义 MAX(o,l,r)=max{sum(o)−sum(t−1) | l≤t≤r}MAX(o,l,r)=max{sum(o)−sum(t−1) | l≤t≤r} ,即以 oo 为右端点,左端点范围是 [l,r][l,r] 的最大子段。求 sum()sum() 就用前面说的前缀和。可以看出,oo 的位置是固定的。所以 sum(o)sum(o) 也是固定的。所以我们要求这个的最大值,只要 sum(t−1)sum(t−1) 最小就可以了。即要求 sum(t−1)sum(t−1) 在 [l,r][l,r] 中的最小值,那怎么快速地求出这个最大值呢?很显然,这不是 RMQRMQ 的活么。对 RMQRMQ 不熟悉的可以参考这 。当然,具体计算的时候还要看看下界 ll 是否低于了 11 。

    接下来想怎么贪心。我们可以每次都选最优的子段,这样选 kk 次显然就是我们所要的结果,那怎么找到最优解呢?用堆来将解存进去,每次堆顶的元素就是最优解。

    考虑一个三元组 (o,l,r)(o,l,r) 表示以 oo 为右端点,左端点的选择区间为 [l,r][l,r] 的 情况,我用了一个 structstruct 来表示这个三元组,但往往实际上每个情况需要额外记录这个情况的最优解 tt ,这个不麻烦,在 structstruct 里面敲一个构造函数就可以了。

    我们假设当前最大的三元组是 (o,l,r)(o,l,r) 。最优解位置是 tt 。ansans 累加这个三元组的贡献。由于 tt 已经被选中,对于这个 oo, tt 已经不能重复选中,但最优解还可能存在于 tt 左右的两端区间中,所以提取出 (o,l,r)(o,l,r) 之后,为了避免重复且不丧失其他较优解,我们仍然要把 (o,l,t−1)(o,l,t−1) , (o,t+1,r)(o,t+1,r) 扔回堆里面去。显然地,在放回去之前应该保证区间的存在,即 l=tl=t 或 r=tr=t 的情况要进行特判。

    最后实现的时候还要注意一点,RMQRMQ 原本数组里面记录的是最优解的值,但我们查询区间最大值的时候查询的是最优解的位置。所以数组里面存的是最优解的位置,要特殊处理。

    CodeCode
    #include<cmath>
    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #include<queue>
    #define MAXN 500005
    #define LOG 20
    #define max(x, y) ((x) > (y) ? (x) : (y))
    #define min(x, y) ((x) < (y) ? (x) : (y))
    long long sum[MAXN], ST[MAXN][LOG];
    namespace RMQ {
    int calc(int x, int y) {
    return sum[x - 1] < sum[y - 1] ? x : y;
    }
    void init(int n) {
    for (int i = 1; i <= n; i++) ST[i][0] = i;
    for (int j = 1; (1 << j) <= n; j++)
    for (int i = 1; i + (1 << j) - 1 <= n; i++)
    ST[i][j] = calc(ST[i][j - 1], ST[i + (1 << (j - 1))][j - 1]);
    }
    int query(int l, int r) {
    int k = log2(r - l + 1);
    return calc(ST[l][k], ST[r - (1 << k) + 1][k]);
    }
    }
    struct element {
    int o, l, r, t;
    element() {}
    element(int o, int l, int r) : o(o), l(l), r(r), t(RMQ::query(l, r)) {}
    friend bool operator < (const element& a, const element& b) {
    return sum[a.o] - sum[a.t - 1] < sum[b.o] - sum[b.t - 1];
    }
    };
    std::priority_queue< element > Q;
    int main() {
    int n, k, L, R;
    scanf("%d%d%d%d", &n, &k, &L, &R);
    for (int i = 1; i <= n; i++) {
    scanf("%lld", &sum[i]);
    sum[i] += sum[i - 1];
    }
    RMQ::init(n);
    for (int i = L; i <= n; i++)
    Q.push(element(i, max(1, i - R + 1), 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 += sum[o] - sum[t - 1];
    if (l != t) Q.push(element(o, l, t - 1));
    if (t != r) Q.push(element(o, t + 1, r));
    }
    printf("%lld ", ans);
    return 0;
    }
    ————————————————
    版权声明:本文为CSDN博主「Nekroz_」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
    原文链接:https://blog.csdn.net/diogenes_/article/details/80820571

    停留是刹那,转身是天涯
  • 相关阅读:
    php的迭代器
    memcache学习使用
    php数组操作小结
    ThinkPHP-3.2.3学习
    正则
    PHP中$_FILES的使用方法及注意事项说明
    子进程管理模块subprocess
    atexit模块解析
    GNU自动补全模块readline解析
    python命令行解析工具argparse模块【3】
  • 原文地址:https://www.cnblogs.com/aprincess/p/11621529.html
Copyright © 2020-2023  润新知