JZOJ5409. Fantasy && Luogu2048 [NOI2010]超级钢琴

题目大意

给出一个序列和\(L, R\), 求前k大长度在\([L,R]\)之间的连续子序列的和的和.

解题思路

朴素的想法是对于一个左端点\(p\), 它的右区间取值范围是一个连续的区间即\([p+L-1,p+R-1]\). 枚举这些区间的和然后排序一下什么的, 当然可以用前缀和优化.

考虑对于一个左端点和一个右端点的区间组成的三元组, 设它的答案是\(f(p,l,r)\),那么\(f(p,l,r)\)是一个固定的数, 即\(f(p,l,r)=max(sum(p,r)), \text{其中}(r\in [p+L-1,p+R-1])\).
转化为前缀和后, \(sum(p,r)=sum_r-sum_{p-1}\), 由于\(sum_{p-1}\)一定, 其实就是\(sum_r\)最大的\(r\)最优. 于是这样的最优的\(r\)可以用ST表在\(O(1)\)的时间内求出.

实际上对于每一个左端点都有一个类似的三元组, 考虑如何能把最大的前\(k\)个子序列求出.
若此时\(f(p,l,r)\)(设\([l,r]\)中最优的端点是\(w\))最大, 将\(f(p,l,r)\)累加到答案的同时, \([l,r]\)这个右端点区间会分裂成两个区间\([l,w)\)\((w,r]\)(前提是这样的区间存在).
为什么? \(w\)只是\([l,r]\)中最大的右端点, 实际上次大的, 第三大的..都能作为可能的答案.

根据上面的分析, 解法已经呼然欲出: 我们维护一个大根堆, 每次取出最大的三元组, 累加答案并分裂区间加入堆. 由于一共只会分裂\(k\)次的区间, 所以堆中的元素不超过\(n+k\)个.
加上预处理, 时间复杂度\(O(n\log{n}+k\log{(n+k)})\).

后记

自己的数据结构真的好差.. 某些大佬(pzr等人)用主席树也能过, 我又不会....

PS:洛谷上的数据范围比标程要大一些

#include <queue>
#include <cstdio>
#include <cstring>
#define W 17
#define N 100010
#define ll long long
#define fo(i, a, b) for(int i = (a); i <= (b); ++i)
#define fd(i, a, b) for(int i = (a); i >= (b); --i)
using namespace std;
inline int min(int a, int b){return a < b ? a : b;}
inline int read()
{
	int x = 0; char ch = getchar(); bool ne = 0;
	while(ch < '0' || ch > '9')	ne |= (ch == '-'), ch = getchar();
	while(ch >= '0' && ch <= '9')	x = (x << 3) + (x << 1) + (ch ^ 48), ch = getchar();
	return ne ? -x : x;
}
int n, k, L, R, lg[N], p2[W + 3] = {1}, sum[N];
ll ans;
namespace ST
{
	int s[N][W + 3];
	void init()
	{
		fo(i, 1, n)	s[i][0] = i;
		fo(j, 1, lg[n])
			fo(i, 1, n - p2[j] + 1)
			{
				int x = s[i][j - 1], y = s[i + p2[j - 1]][j - 1];
				s[i][j] = sum[x] > sum[y] ? x : y;
			}
	}
	inline int query(int l, int r)
	{
		int p = lg[r - l + 1];
		int x = s[l][p], y = s[r - p2[p] + 1][p];
		return sum[x] > sum[y] ? x : y;
	}
}
struct Node
{
	int p, l, r, w;
	Node(){}
	Node(int _p, int _l, int _r){p = _p, l = _l, r = _r, w = ST::query(l, r);}
	inline int val() const {return sum[w] - sum[p - 1];}
	bool operator<(const Node b) const
	{
		return val() < b.val();
	}
};
priority_queue<Node> h;
int main()
{
	freopen("fantasy.in", "r", stdin);
	freopen("fantasy.out", "w", stdout);
	n = read(), k = read(), L = read(), R = read();
	fo(i, 1, W)	p2[i] = p2[i - 1] << 1;
	fo(i, 1, n)	sum[i] = read() + sum[i - 1];
	fo(i, 2, n)	lg[i] = lg[i >> 1] + 1;
	ST::init();
	fo(i, 1, n - L + 1)
		h.push(Node(i, i + L - 1, min(n, i + R - 1)));
	fo(i, 1, k)
	{
		Node t = h.top(); h.pop();
		ans += t.val(); 
		if(t.l < t.w)	h.push(Node(t.p, t.l, t.w - 1));
		if(t.w < t.r)	h.push(Node(t.p, t.w + 1, t.r));
	}
	printf("%lld", ans);
	return 0;
}
posted @ 2020-12-26 15:12  Martin_MHT  阅读(81)  评论(0)    收藏  举报