标签: RMQ

[NOI2010]超级钢琴 题解

[NOI2010]超级钢琴 题解

题目地址:洛谷:【P2048】[NOI2010]超级钢琴 – 洛谷、BZOJ:Problem 2006. — [NOI2010]超级钢琴

题目描述

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

输入输出格式

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

输出格式:
输出只有一个整数,表示乐曲美妙度的最大值。

输入输出样例

输入样例#1:

4 3 2 3
3
2
-6
8

输出样例#1:

11

说明

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

  1. 音符1 ~ 2,美妙度为3 + 2 = 5
  2. 音符2 ~ 3,美妙度为2 + (-6) = -4
  3. 音符3 ~ 4,美妙度为(-6) + 8 = 2
  4. 音符1 ~ 3,美妙度为3 + 2 + (-6) = -1
  5. 音符2 ~ 4,美妙度为2 + (-6) + 8 = 4

最优方案为:乐曲由和弦1,和弦3,和弦5组成,美妙度为5 + 2 + 4 = 11。+
所有数据满足:-1000 ≤ Ai ≤ 1000,1 ≤ L ≤ R ≤ n且保证一定存在满足要求的乐曲。

题解

暴力可过的点1~2,我们考虑枚举每个长度在[L, R]范围内的区间,将其和加入一个堆,并使堆的元素数量始终不超过k,以确保不爆空间即可。实测这个方法在开O2的情况下还能过4。复杂度O(n(R-L+1) \log n)
点3有性质k=1,也就是说只用查最大值,考虑把所有元素开个ST表存起来,对于确定的区间左端点i,查询最大值的区间应当是[i+L-1, i+R-1],只需要在这个区间内做RMQ后,减去[1, i-1]的元素求和即可,这里可以预处理一个前缀和。枚举区间左端点即可,复杂度O(n \log n)
如果将这个方法应用于求k大,我们考虑这样的一个信息(i, l, r, mx)表示选择区间左端点为i,且查询最大值的区间为[l, r],这个区间中取得最大值的位置是mx,我们先类似点3的做法将所有信息插入堆中,然后从堆顶拿最大值以后将该信息拆分成(i, l, mx-1, newmx1)和(i, mx+1, r, newmx2)两个子信息,RMQ出来newmx1和newmx2,插回堆里,就能达成我们想要的效果。这个算法的复杂度是O(n \log n)
模拟考的时候只想到了点3的做法,并没想到可以用堆来推广做,感觉到好亏啊QAQ,明明离AC那么近。

代码

// Code by KSkun, 2018/5
#include <cstdio>
#include <cctype>
#include <cmath>

#include <algorithm>
#include <queue>

typedef long long LL;

inline char fgc() {
    static char buf[100000], *p1 = buf, *p2 = buf;
    return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 100000, stdin), p1 == p2) ? EOF 
        : *p1++;
}

inline LL readint() {
    register LL res = 0, neg = 1;
    register char c = fgc();
    while(!isdigit(c)) {
        if(c == '-') neg = -1;
        c = fgc();
    }
    while(isdigit(c)) {
        res = (res << 1) + (res << 3) + c - '0';
        c = fgc();
    }
    return res * neg;
}

const int MAXN = 500005;

LL n, k, L, R, a[MAXN];

LL mx[MAXN][20], mxd[MAXN][20], pow2[MAXN];

struct Node {
    LL val;
    int beg, l, r, mx;
    inline bool operator<(const Node &rhs) const {
        return val < rhs.val;
    }
};

std::priority_queue<Node> pq;

inline int querymx(int l, int r) {
    int k = floor(log(r - l + 1) / log(2));
    if(mx[l][k] > mx[r - pow2[k] + 1][k]) {
        return mxd[l][k];
    } else {
        return mxd[r - pow2[k] + 1][k];
    }
}

int main() {
    n = readint(); k = readint(); L = readint(); R = readint();
    for(int i = 1; i <= n; i++) {
        a[i] = readint() + a[i - 1];
    }
    for(int i = 1; i <= n; i++) {
        mx[i][0] = a[i];
        mxd[i][0] = i;
    }
    pow2[0] = 1;
    for(int i = 1; i <= 20; i++) {
        pow2[i] = pow2[i - 1] << 1;
    }
    for(int i = 1; i <= 20; i++) {
        for(int j = 1; j + pow2[i] - 1 <= n; j++) {
            if(mx[j][i - 1] > mx[j + pow2[i - 1]][i - 1]) {
                mx[j][i] = mx[j][i - 1];
                mxd[j][i] = mxd[j][i - 1];
            } else {
                mx[j][i] = mx[j + pow2[i - 1]][i - 1];
                mxd[j][i] = mxd[j + pow2[i - 1]][i - 1];
            }
        }
    }
    for(int i = 1; i + L - 1 <= n; i++) {
        int l = i + L - 1, r = std::min(i + R - 1, n);
        pq.push(Node {a[querymx(l, r)] - a[i - 1], i, l, r, querymx(l, r)});
    }
    LL ans = 0;
    while(k--) {
        Node t = pq.top(); pq.pop();
        ans += t.val;
        if(t.l == t.r) continue;
        if(t.l != t.mx) pq.push(Node {a[querymx(t.l, t.mx - 1)] - a[t.beg - 1], 
            t.beg, t.l, t.mx - 1, querymx(t.l, t.mx - 1)});
        if(t.r != t.mx) pq.push(Node {a[querymx(t.mx + 1, t.r)] - a[t.beg - 1], 
            t.beg, t.mx + 1, t.r, querymx(t.mx + 1, t.r)});
    }
    printf("%lld", ans);
    return 0;
}