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;
}

posted @ 2018-08-14 21:15  谜のNOIP  阅读(94)  评论(0编辑  收藏  举报