[Luogu P2048] [BZOJ 2006] [NOI2010]超级钢琴
程序员文章站
2022-07-13 11:58:21
...
洛谷传送门
BZOJ传送门
题目描述
小Z是一个小有名气的钢琴家,最近C博士送给了小Z一架超级钢琴,小Z希望能够用这架钢琴创作出世界上最美妙的音乐。
这架超级钢琴可以弹奏出个音符,编号为至。第i个音符的美妙度为,其中可正可负。
一个“超级和弦”由若干个编号连续的音符组成,包含的音符个数不少于且不多于。我们定义超级和弦的美妙度为其包含的所有音符的美妙度之和。两个超级和弦被认为是相同的,当且仅当这两个超级和弦所包含的音符集合是相同的。
小Z决定创作一首由个超级和弦组成的乐曲,为了使得乐曲更加动听,小Z要求该乐曲由个不同的超级和弦组成。我们定义一首乐曲的美妙度为其所包含的所有超级和弦的美妙度之和。小Z想知道他能够创作出来的乐曲美妙度最大值是多少。
输入输出格式
输入格式:
输入第一行包含四个正整数。其中为音符的个数,为乐曲所包含的超级和弦个数,和分别是超级和弦所包含音符个数的下限和上限。
接下来行,每行包含一个整数,表示按编号从小到大每个音符的美妙度。
输出格式:
输出只有一个整数,表示乐曲美妙度的最大值。
输入输出样例
输入样例#1:
4 3 2 3
3
2
-6
8
输出样例#1:
11
说明
共有5种不同的超级和弦:
1. 音符1 ~ 2,美妙度为3 + 2 = 5
2. 音符2 ~ 3,美妙度为2 + (-6) = -4
3. 音符3 ~ 4,美妙度为(-6) + 8 = 2
4. 音符1 ~ 3,美妙度为3 + 2 + (-6) = -1
5. 音符2 ~ 4,美妙度为2 + (-6) + 8 = 4
最优方案为:乐曲由和弦1,和弦3,和弦5组成,美妙度为。
所有数据满足:且保证一定存在满足要求的乐曲。
解题分析
最暴力的做法就是提出个区间然后排序找出前小了, 然而会成狗…
既然我们不能保存所有的区间, 我们就先提出一些最优的。 可以发现以某个点为左端点的区间的最优值共有个, 那么我们对于每个左端点保存一个最优值, 以及当前的位置、 合法的区间。 每次取出堆顶更新答案, 然后划分左右的合法区间重新加入堆中。
关于的部分用表即可。
总复杂度。
代码如下:
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <cctype>
#include <algorithm>
#include <queue>
#define R register
#define IN inline
#define gc getchar()
#define W while
#define MX 500050
#define ll long long
bool neg;
template <class T>
IN void in(T &x)
{
x = 0; R char c = gc;
for (; !isdigit(c); c = gc)
if(c == '-') neg = true;
for (; isdigit(c); c = gc)
x = (x << 1) + (x << 3) + c - 48;
if(neg) neg = false, x = -x;
}
int dot, l, r, k, bd;
ll pre[MX], st[20][MX], ans;
struct INFO {ll val; int st, lef, rig, pos;};
IN bool operator < (const INFO &x, const INFO &y) {return x.val < y.val;}
std::priority_queue <INFO> pq;
IN void init()
{
bd = log2(dot); int step, ed;
for (R int i = 1; i <= dot; ++i) st[0][i] = i;
for (R int t = 1; t <= bd; ++t)
{
step = 1 << t - 1; ed = dot - (step << 1) + 1;
for (R int i = 1; i <= ed; ++i)
st[t][i] = pre[st[t - 1][i]] > pre[st[t - 1][i + step]] ? st[t - 1][i] : st[t - 1][i + step];
}
}
IN int query(R int lef, R int rig)
{
int seg = log2(rig - lef + 1);
return pre[st[seg][lef]] > pre[st[seg][rig - (1 << seg) + 1]] ? st[seg][lef] : st[seg][rig - (1 << seg) + 1];
}
int main(void)
{
int st, ed, tar;
INFO cur;
in(dot), in(k), in(l), in(r);
for (R int i = 1; i <= dot; ++i) in(pre[i]), pre[i] += pre[i - 1];
init();
for (R int i = 1; i <= dot; ++i)
{
st = i + l - 1, ed = i + r - 1;
ed = std::min(ed, dot);
if(st > ed) break;
tar = query(st, ed);
pq.push({pre[tar] - pre[i - 1], i, st, ed, tar});
}
W(k--)
{
cur = pq.top(); pq.pop();
ans += cur.val;
st = cur.pos - 1, ed = cur.pos + 1;
if(st >= cur.lef)
{
tar = query(cur.lef, st);
pq.push({pre[tar] - pre[cur.st - 1], cur.st, cur.lef, st, tar});
}
if(ed <= cur.rig)
{
tar = query(ed, cur.rig);
pq.push({pre[tar] - pre[cur.st - 1], cur.st, ed, cur.rig, tar});
}
}
printf("%lld", ans);
}