[luoguP2048/NOI2010] 超级钢琴
sol
所求为连续值之和,因此先计算前缀和 \(s\),那么固定左端点 \(i\) 后,最大的连续和即为 \(-s_{i-1} + \max_{j=i + l - 1}^{\min\{i+r-1,n\}} s_j\),使用 ST 表即可计算出最大区间和。由于该左端点仍有可能对答案产生贡献,因此在取走某个答案后,需要将其长度范围分为两半,可以需要使用堆来动态解决。
代码
#include <iostream>
#include <algorithm>
#include <cstring>
#include <queue>
using namespace std;
typedef long long LL;
const int N = 500005, K = 19;
int n, k, l, r;
int a[N], s[N];
int st[N][K + 1], pos[N][K + 1];
int lg2[N];
struct Node {
int u, l, r, k;
bool operator< (const Node &W) const {
return s[k] - s[u - 1] < s[W.k] - s[W.u - 1];
}
};
void init(){
for (int i = 1; i <= n; i ++ )
st[i][0] = s[i], pos[i][0] = i;
for (int i = 2; i <= n; i ++ ) lg2[i] = lg2[i >> 1] + 1;
for (int k = 1; k <= K; k ++ ) {
for (int l = 1; l + (1 << k) - 1 <= n; l ++ ) {
int posr = l + (1 << k - 1);
if (st[l][k - 1] >= st[posr][k - 1])
st[l][k] = st[l][k - 1], pos[l][k] = pos[l][k - 1];
else
st[l][k] = st[posr][k - 1], pos[l][k] = pos[posr][k - 1];
}
}
}
int get_max(int l, int r){
int len = r - l + 1;
if (st[l][lg2[len]] >= st[r - (1 << lg2[len]) + 1][lg2[len]])
return pos[l][lg2[len]];
else
return pos[r - (1 << lg2[len]) + 1][lg2[len]];
}
int main(){
scanf("%d%d%d%d", &n, &k, &l, &r);
for (int i = 1; i <= n; i ++ ) scanf("%d", &a[i]), s[i] = s[i - 1] + a[i];
init();
priority_queue<Node> heap;
for (int i = 1; i + l - 1 <= n; i ++ ) heap.push({i, l, min(r, n - i + 1), get_max(i + l - 1, min(n, i + r - 1))});
LL res = 0;
while (k -- ) {
Node t = heap.top();
heap.pop();
res += s[t.k] - s[t.u - 1];
int len = t.k - t.u + 1;
if (len != t.l) heap.push({t.u, t.l, len - 1, get_max(t.u + t.l - 1, t.u + len - 2)});
if (len != t.r) heap.push({t.u, len + 1, t.r, get_max(t.u + len, t.u + t.r - 1)});
}
printf("%lld\n", res);
}

浙公网安备 33010602011771号