[BZOJ2006][NOI2010]超级钢琴
试题描述
小 Z 是一个小有名气的钢琴家,最近 C 博士送给了小 Z 一架超级钢琴,小 Z 希望能够用这架钢琴创作出世界上最美妙的音乐。
这架超级钢琴可以弹奏出 \(n\) 个音符,编号为 \(1\) 至 \(n\)。第 \(i\) 个音符的美妙度为 \(A_i\),其中 \(A_i\) 可正可负。 一个“超级和弦”由若干个编号连续的音符组成,包含的音符个数不少于 \(L\) 且不多于 \(R\) 。我们定义超级和弦的美妙度为其包含的所有音符的美妙度之和。两个超级和弦被认为是相同的,当且仅当这两个超级和弦所包含的音符集合是相同的。
小 Z 决定创作一首由 \(k\) 个超级和弦组成的乐曲,为了使得乐曲更加动听,小 Z 要求该乐曲由 \(k\) 个不同的超级和弦组成。
我们定义一首乐曲的美妙度为其所包含的所有超级和弦的美妙度之和。小 Z 想知道他能够创作出来的乐曲美妙度最大值是多少。
输入
第一行包含四个正整数 \(n, k, L, R\)。其中 \(n\) 为音符的个数, \(k\) 为乐曲所包含的超级和弦个数, \(L\) 和 \(R\) 分别是超级和弦所包含音符个数的下限和上限。
接下来 \(n\) 行,每行包含一个整数 \(A_i\),表示按编号从小到大每个音符的美妙度。
输出
一共包 \(Q+1\) 行,依次为 \(Ans_0\)、\(Ans_1\)、……、\(Ans_Q\)。
输入示例
4 3 2 3
3
2
-6
8
输出示例
11
数据规模及约定
\(N \le 500,000\)
\(k \le 500,000\)
\(-1000 \le A_i \le 1000,1 \le L \le R \le N\) 且保证一定存在满足条件的乐曲
题解
对于每一个确定的左端点 \(i\),右端点在一个区间 \([L_i, R_i]\) 内。那么如果要求最大值,就是先求出 \(A_i\) 的前缀和 \(S_i\);枚举 \(i\),然后找到满足 \(j \in [L_i, R_i]\) 中最大的 \(S_j\),用 \(S_j - S_{i-1}\) 更新答案。
现在要求前 \(k\) 大,我们把上面的过程中找到的每一对 \(S_j - S_{i-1}\) 放到一个堆里,取出一个后相当于把右端点所在的区间分裂成两个区间,然后在新分裂的两个区间中分别找到前缀和的最大值扔到堆里就好了。这样重复 \(k\) 次,累加即可;堆中的元素个数每次增加 \(1\)。
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <algorithm>
#include <queue>
using namespace std;
#define rep(i, s, t) for(int i = (s), mi = (t); i <= mi; i++)
#define dwn(i, s, t) for(int i = (s), mi = (t); i >= mi; i--)
int read() {
int x = 0, f = 1; char c = getchar();
while(!isdigit(c)){ if(c == ‘-‘) f = -1; c = getchar(); }
while(isdigit(c)){ x = x * 10 + c - ‘0‘; c = getchar(); }
return x * f;
}
#define maxn 500010
#define maxlog 19
#define LL long long
int n, K, L, R, S[maxn];
struct Info {
int l, rl, rr, optr;
Info() {}
Info(int _1, int _2, int _3, int _4): l(_1), rl(_2), rr(_3), optr(_4) {}
bool operator < (const Info& t) const { return S[optr] - S[l-1] < S[t.optr] - S[t.l-1]; }
};
priority_queue <Info> Q;
int Log[maxn], mxp[maxlog][maxn];
void init() {
Log[1] = 0;
rep(i, 2, n) Log[i] = Log[i>>1] + 1;
rep(i, 1, n) mxp[0][i] = i;
for(int j = 1; (1 << j) <= n; j++)
for(int i = 1; i + (1 << j) - 1 <= n; i++) {
int a = mxp[j-1][i], b = mxp[j-1][i+(1<<j>>1)];
mxp[j][i] = S[a] > S[b] ? a : b;
}
return ;
}
int query(int ql, int qr) {
int t = Log[qr-ql+1], a = mxp[t][ql], b = mxp[t][qr-(1<<t)+1];
return S[a] > S[b] ? a : b;
}
int main() {
n = read(); K = read(); L = read(); R = read();
rep(i, 1, n) S[i] = S[i-1] + read();
init();
rep(i, 1, n) {
if(i + L - 1 > n) break;
int rl = i + L - 1, rr = min(n, i + R - 1), optr = query(rl, rr);
Q.push(Info(i, rl, rr, optr));
}
LL ans = 0;
rep(i, 1, K) {
Info u = Q.top(); Q.pop();
ans += S[u.optr] - S[u.l-1];
if(u.rl < u.optr) {
int opt = query(u.rl, u.optr - 1);
Q.push(Info(u.l, u.rl, u.optr - 1, opt));
}
if(u.optr < u.rr) {
int opt = query(u.optr + 1, u.rr);
Q.push(Info(u.l, u.optr + 1, u.rr, opt));
}
}
printf("%lld\n", ans);
return 0;
}