bzoj 2006 [NOI2010]超级钢琴 贪心+RMQ+堆
题面
解法
感觉这道题解法妙妙的
考虑这样一个贪心,记一个三元组\((x,l,r)\)表示当前和弦的左端点为\(x\),右端点在区间\([l,r]\)
那么这个三元组对应的和弦最大值为\(max(s_l,…s_r)-s_{x-1}\),其中,\(s_i\)表示前缀和
考虑将这些三元组扔进一个大根堆里,按照和弦的大小作为关键字
然后取出堆顶的时候,找到\(s_l\)到\(s_r\)中最大的前缀和对应位置\(pos\),向堆中加入新的2个三元组\((x,l,pos-1),(x,pos+1,r)\)
然后不断这样做就可以了
查询区间最大值可以用RMQ实现\(O(1)\)查询
时间复杂度:\(O(n\ log\ n+k\ log\ n)\)
代码
#include <bits/stdc++.h>
#define int long long
#define N 500010
using namespace std;
template <typename node> void chkmax(node &x, node y) {x = max(x, y);}
template <typename node> void chkmin(node &x, node y) {x = min(x, y);}
template <typename node> void read(node &x) {
x = 0; int f = 1; char c = getchar();
while (!isdigit(c)) {if (c == '-') f = -1; c = getchar();}
while (isdigit(c)) x = x * 10 + c - '0', c = getchar(); x *= f;
}
struct Node {
int x, l, r, val;
bool operator > (const Node &a) const {
return val < a.val;
}
};
int f[N][21], g[N][21];
int calc(int l, int r) {
int t = log2(r - l + 1);
return max(f[l][t], f[r - (1 << t) + 1][t]);
}
int Find(int l, int r) {
int t = log2(r - l + 1);
if (f[l][t] > f[r - (1 << t) + 1][t]) return g[l][t];
return g[r - (1 << t) + 1][t];
}
main() {
int n, m, l, r;
read(n), read(m), read(l), read(r);
for (int i = 1; i <= n; i++) {
int x; read(x);
f[i][0] = f[i - 1][0] + x, g[i][0] = i;
}
for (int j = 1; (1 << j) <= n; j++)
for (int i = 1; i + (1 << j) - 1 <= n; i++) {
int x = f[i][j - 1], y = f[i + (1 << j - 1)][j - 1];
f[i][j] = max(x, y);
if (x > y) g[i][j] = g[i][j - 1];
else g[i][j] = g[i + (1 << j - 1)][j - 1];
}
priority_queue <Node, vector <Node>, greater <Node> > h;
for (int i = 1; i <= n - l + 1; i++) {
int tl = i + l - 1, tr = min(n, i + r - 1);
int val = calc(tl, tr);
h.push((Node) {i, tl, tr, val - f[i - 1][0]});
}
int ans = 0;
while (m--) {
Node tmp = h.top(); h.pop();
int tl = tmp.l, tr = tmp.r;
int pos = Find(tl, tr); ans += tmp.val;
if (pos != tl) h.push((Node) {tmp.x, tl, pos - 1, calc(tl, pos - 1) - f[tmp.x - 1][0]});
if (pos != tr) h.push((Node) {tmp.x, pos + 1, tr, calc(pos + 1, tr) - f[tmp.x - 1][0]});
}
cout << ans << "\n";
return 0;
}