[NOI2010]超级钢琴题解

[NOI2010]超级钢琴题解

很妙的一道题,用到了 贪心 + 堆 + ST表

先考虑 k=1k=1 的方法。

k=1k=1

先对原序列前缀和。

然后枚举左端点,ans=maxr=l+a1l+b1(srsl1)ans = \max_{r = l + a - 1}^{l + b - 1} (s_r - s_{l - 1}), 如果要让 ansans 最大,只需让 srs_r 最大即可, 区间最大值可以用 STST表 来求。复杂度 O(n)O(n)

正解

像这种要求前 kk 大, 目前会求最大的情况下可以考虑使用堆。

设五元组 (x,l,r,p,mx)(x, l, r, p, mx) 表示 (左端点的位置(xx),右端点所在区间(l,rl,r),最优右端点位置(pp),最大值(mxmx))

mxmx 为权值,维护大根堆。

对于每个点作为左端点都可以得到一个五元组,把它们都放到堆中。

堆顶即为当前最大值。

取出堆顶后,由于已经选了 (x,p)(x,p) 所以把它分成两个五元组 (x,l,p1,pl,mxl)(x,l,p-1,p_l,mx_l)(x,p+1,r,pr,mxr)(x,p+1,r,p_r,mx_r), 并放入堆中, pl,mxl,pr,mxrp_l,mx_l,p_r,mx_r 可以用 STST表 求出。

把这个操作重复 kk 次,每次结果的和即为所求。

Code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
#include <iostream>
#include <cstdio>
#include <queue>
#define int long long

using namespace std;

const int N = 5e5 + 10;
int st[N][30], d[N][30];

int n, k, l, r;
int a[N], lg[N];

int find(int x, int y)
{
int k = lg[y - x + 1];
if (st[x][k] > st[y - (1 << k) + 1][k])
{
return d[x][k];
}
else return d[y - (1 << k) + 1][k];
}

struct Node
{
int x, l, r, p;

bool operator < (const Node b) const
{
return a[find(l, r)] - a[x - 1] < a[find(b.l, b.r)] - a[b.x - 1];
}
};

priority_queue<Node> q;

signed main()
{
scanf("%lld%lld%lld%lld", &n, &k, &l, &r);
lg[1] = 0;
for (int i = 1; i <= n; ++i)
{
scanf("%lld", &a[i]);
a[i] = a[i - 1] + a[i];
st[i][0] = a[i];
d[i][0] = i;
}
for (int i = 2; i <= n; ++i)
{
lg[i] = lg[i / 2] + 1;
}
for (int k = 1; k <= lg[n]; ++k)
{
for (int i = 1; i + (1ll << k) - 1 <= n; ++i)
{
if (st[i][k - 1] > st[i + (1ll << (k - 1))][k - 1])
{
st[i][k] = st[i][k - 1];
d[i][k] = d[i][k - 1];
}
else
{
st[i][k] = st[i + (1ll << (k - 1))][k - 1];
d[i][k] = d[i + (1ll << (k - 1))][k - 1];
}
}
}
for (int i = 1; i <= n - l + 1; ++i)
{
q.push((Node){i, min(n, i + l - 1), min(n, i + r - 1), find(min(n, i + l - 1), min(n, i + r - 1))});
}
int ans = 0;
while (k--)
{
Node u = q.top();
q.pop();
ans += a[u.p] - a[u.x - 1];
if (u.l <= u.p - 1) q.push((Node){u.x, u.l, u.p - 1, find(u.l, u.p - 1)});
if (u.p + 1 <= u.r) q.push((Node){u.x, u.p + 1, u.r, find(u.p + 1, u.r)});
}
cout <<ans << endl;
return 0;
}