BZOJ 2006: [NOI2010]超级钢琴

2023-07-30,,

2006: [NOI2010]超级钢琴

Time Limit: 20 Sec  Memory Limit: 552 MB
Submit: 2613  Solved: 1297
[Submit][Status][Discuss]

Description

小Z是一个小有名气的钢琴家,最近C博士送给了小Z一架超级钢琴,小Z希望能够用这架钢琴创作出世界上最美妙的
音乐。 这架超级钢琴可以弹奏出n个音符,编号为1至n。第i个音符的美妙度为Ai,其中Ai可正可负。 一个“超级
和弦”由若干个编号连续的音符组成,包含的音符个数不少于L且不多于R。我们定义超级和弦的美妙度为其包含的
所有音符的美妙度之和。两个超级和弦被认为是相同的,当且仅当这两个超级和弦所包含的音符集合是相同的。 
小Z决定创作一首由k个超级和弦组成的乐曲,为了使得乐曲更加动听,小Z要求该乐曲由k个不同的超级和弦组成。
我们定义一首乐曲的美妙度为其所包含的所有超级和弦的美妙度之和。小Z想知道他能够创作出来的乐曲美妙度最
大值是多少。

Input

第一行包含四个正整数n, k, L, R。其中n为音符的个数,k为乐曲所包含的超级和弦个数,L和R分别是超级和弦所
包含音符个数的下限和上限。 接下来n行,每行包含一个整数Ai,表示按编号从小到大每个音符的美妙度。
N<=500,000
k<=500,000
-1000<=Ai<=1000,1<=L<=R<=N且保证一定存在满足条件的乐曲

Output

只有一个整数,表示乐曲美妙度的最大值。

Sample Input

4 3 2 3
3
2
-6
8

Sample Output

11

【样例说明】
共有5种不同的超级和弦:
音符1 ~ 2,美妙度为3 + 2 = 5
音符2 ~ 3,美妙度为2 + (-6) = -4
音符3 ~ 4,美妙度为(-6) + 8 = 2
音符1 ~ 3,美妙度为3 + 2 + (-6) = -1
音符2 ~ 4,美妙度为2 + (-6) + 8 = 4
最优方案为:乐曲由和弦1,和弦3,和弦5组成,美妙度为5 + 2 + 4 = 11。

HINT

Source

 

[Submit][Status][Discuss]

学习的题解,转载在下——

堆+RMQ

定义一个四元组(i,l,r,t)表示是以第i个为开头的,结尾在l-r之间(满足长度>=L,<=R),且结尾在t的权值和最大。

首先把以每一位开头的四元组加入堆,此时的l,r是恰好满足长度>=L,<=R;

堆中排序的关键字是权值之和。

然后把堆顶取出(i,l,r,t),然后再把(i,l,t-1,t')和(i,t+1,r,t'')加入堆中(因为要满足任意两个序列不同的条件)

那么四元组中的t如何快速求出?

用RMQ即可:维护前缀和,权值和就是sum[t]-sum[i-1],对于用一个四元组sum[i-1]相同,因此只要找到l-r中sum[i]最大的即可。

 #include <queue>
#include <cstdio> inline int nextChar(void)
{
const static int siz = ; static char buf[siz];
static char *hd = buf + siz;
static char *tl = buf + siz; if (hd == tl)
fread(hd = buf, , siz, stdin); return *hd++;
} inline int nextInt(void)
{
register int ret = ;
register int neg = false;
register int bit = nextChar(); for (; bit < ; bit = nextChar())
if (bit == '-')neg ^= true; for (; bit > ; bit = nextChar())
ret = ret * + bit - ; return neg ? -ret : ret;
} const int siz = ; int N, M, L, R; int num[siz];
int sum[siz]; int st[siz][], log[siz]; inline void preworkRMQ(void)
{
for (int i = ; i <= N; ++i)
st[i][] = i; for (int i = ; ( << i) <= N; ++i)
for (int j = ; j + ( << i) - <= N; ++j)
{
int x = st[j][i - ];
int y = st[j + ( << (i - ))][i - ];
st[j][i] = sum[x] > sum[y] ? x : y;
} log[] = -; for (int i = ; i <= N; ++i)
log[i] = log[i >> ] + ;
} inline int query(int l, int r)
{
if (l == r)
return l; int t = log[r - l + ]; int x = st[l][t];
int y = st[r - ( << t) + ][t]; return sum[x] > sum[y] ? x : y;
} struct data
{
int p, t, l, r; data(void) {};
data(int a, int b, int c, int d) :
p(a), t(b), l(c), r(d) {};
}; inline bool operator < (const data &a, const data &b)
{
return sum[a.t] - sum[a.p - ] < sum[b.t] - sum[b.p - ];
} std::priority_queue<data> h; signed main(void)
{
N = nextInt();
M = nextInt();
L = nextInt();
R = nextInt(); for (int i = ; i <= N; ++i)
num[i] = nextInt(); for (int i = ; i <= N; ++i)
sum[i] = sum[i - ] + num[i]; preworkRMQ(); long long ans = ; for (int i = ; i <= N - L + ; ++i)
{
int l = i + L - ;
int r = i + R - ; if (r > N)
r = N; h.push(data(i, query(l, r), l, r));
} for (int i = ; i <= M; ++i)
{
data top = h.top(); h.pop(); ans += sum[top.t] - sum[top.p - ]; if (top.t > top.l)
h.push(data(top.p, query(top.l, top.t - ), top.l, top.t - ));
if (top.t < top.r)
h.push(data(top.p, query(top.t + , top.r), top.t + , top.r));
} printf("%lld\n", ans);
}

@Author: YouSiki

BZOJ 2006: [NOI2010]超级钢琴的相关教程结束。

《BZOJ 2006: [NOI2010]超级钢琴.doc》

下载本文的Word格式文档,以方便收藏与打印。