bzoj2006 [NOI2010]超级钢琴 [优先队列|RMQ]

Description

小Z是一个小有名气的钢琴家,最近C博士送给了小Z一架超级钢琴,小Z希望能够用这架钢琴创作出世界上最美妙的音乐。 这架超级钢琴可以弹奏出n个音符,编号为1至n。第i个音符的美妙度为Ai,其中Ai可正可负。 一个“超级和弦”由若干个编号连续的音符组成,包含的音符个数不少于L且不多于R。我们定义超级和弦的美妙度为其包含的所有音符的美妙度之和。两个超级和弦被认为是相同的,当且仅当这两个超级和弦所包含的音符集合是相同的。 小Z决定创作一首由k个超级和弦组成的乐曲,为了使得乐曲更加动听,小Z要求该乐曲由k个不同的超级和弦组成。我们定义一首乐曲的美妙度为其所包含的所有超级和弦的美妙度之和。小Z想知道他能够创作出来的乐曲美妙度最大值是多少。

Input

第一行包含四个正整数n, k, L, R。其中n为音符的个数,k为乐曲所包含的超级和弦个数,L和R分别是超级和弦所包含音符个数的下限和上限。 接下来n行,每行包含一个整数Ai,表示按编号从小到大每个音符的美妙度。

Output

只有一个整数,表示乐曲美妙度的最大值。

Sample Input

4 3 2 3
3
2
-6
8

Sample Output

11

Hint

共有5种不同的超级和弦:

音符1 ~ 2,美妙度为3 + 2 = 5
音符2 ~ 3,美妙度为2 + (-6) = -4
音符3 ~ 4,美妙度为(-6) + 8 = 2
音符1 ~ 3,美妙度为3 + 2 + (-6) = -1
音符2 ~ 4,美妙度为2 + (-6) + 8 = 4
最优方案为:乐曲由和弦1,和弦3,和弦5组成,美妙度为5 + 2 + 4 = 11。

Solution

先解释一下题目中的“两个超级和弦被认为是相同的,当且仅当这两个超级和弦所包含的音符集合是相同的”
这句话就是说,同一个区间[l, r]不能用多次。假设区间[l1, r1]所包含的元素与[l, r]包括顺序完全相同,这两个区间也是可以同时使用的。
唉就是这句话把我坑了啊……我一开始还想怎么判断两个区间包含的元素相同……结果发现傻逼了……

好了言归正传,说下本题思路:
以开始位置为i的区间,它的结束位置只能是 [i+L1,i+R1] 之间。
假设我们要求以i开始的区间,它的最大得分是多少,即 sum[j]sum[i1] 最大,很显然只要 RMQ(i+L1,i+R1) 求出最大的 sum[j] 即可。
接下来开始本题的关键:

  • 设开始位置为i,结束位置为[l,r],最大得分点为j,最大得分为v,将i,l,r,j,v存入一个节点。
  • 枚举i=1 to n,将所有节点放进一个优先队列。那么此时队首的元素就是得分最大的区间。
  • 依次将队首元素取出,此时对于点i来说,点j已经不能用了。原区间分裂成 [l,j1] , [j+1,r] 两个区间,我们再用RMQ分别算出新区间的v和j,将其加入优先队列。
  • 重复以上步骤直到取出了k个元素。

最后再膜拜一下手写堆的巨巨orz……

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <cmath>
#include <queue>

using namespace std;

const int MAXN = 500005;

int n, k, minlen, maxlen;
long long arr[MAXN];
long long sum[MAXN];

int ST[MAXN][20];
int IDX[MAXN][20];
void initST() {
    for (int i = 1; i <= n; i++) {
        ST[i][0] = sum[i];
        IDX[i][0] = i;
    }
    for (int j = 1; j <= 20; j++)
        for (int i = 1; i <= n; i++)
            if (i + (1 << j) - 1 <= n) {
                if (ST[i][j - 1] > ST[i + (1 << j - 1)][j - 1]) {
                    ST[i][j] = ST[i][j - 1];
                    IDX[i][j] = IDX[i][j - 1];
                } else {
                    ST[i][j] = ST[i + (1 << j - 1)][j - 1];
                    IDX[i][j] = IDX[i + (1 << j - 1)][j - 1];
                }
            }
}
int query(int a, int b) {
    int k = log(b - a + 1) / log(2);
    return max(ST[a][k], ST[b - (1 << k) + 1][k]);
}
int query_idx(int a, int b) {
    int k = log(b - a + 1) / log(2);
    if (ST[a][k] > ST[b - (1 << k) + 1][k]) return IDX[a][k];
    else return IDX[b - (1 << k) + 1][k];
}

struct Node{
    int i, idx, l, r, v;
    Node() {}
    Node(int a, int b, int c, int d, int e)
        : i(a), idx(b), l(c), r(d), v(e) {}
    bool operator < (const Node &n) const {
        return v < n.v;
    }
};

priority_queue<Node> q;

int main() {
    scanf("%d %d %d %d", &n, &k, &minlen, &maxlen);
    for (int i = 1; i <= n; i++) {
        scanf("%lld", &arr[i]);
        sum[i] = sum[i - 1] + arr[i];
    }
    initST();
    for (int i = 1; i + minlen - 1 <= n; i++) {
        int l = i + minlen - 1;
        int r = min(n, i + maxlen - 1);
        int v = query(l, r);
        int idx = query_idx(l, r);
        q.push(Node(i, idx, l, r, v - sum[i - 1]));
    }
    long long ans = 0;
    while (k--) {
        Node cur = q.top();
        q.pop();
        ans += cur.v;
        if (cur.idx > cur.l) {
            int l = cur.l;
            int r = cur.idx - 1;
            int v = query(l, r);
            int idx = query_idx(l, r);
            q.push(Node(cur.i, idx, l, r, v - sum[cur.i - 1]));
        }
        if (cur.idx < cur.r) {
            int l = cur.idx + 1;
            int r = cur.r;
            int v = query(l, r);
            int idx = query_idx(l, r);
            q.push(Node(cur.i, idx, l, r, v - sum[cur.i - 1]));
        }
    }
    printf("%lld\n", ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值