2006: [NOI2010]超级钢琴
Time Limit: 20 Sec Memory Limit: 552 MBSubmit: 2565 Solved: 1269
[ 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
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。
【样例说明】
共有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
【题解】【st表+优先队列】
【用st表维护每个区间的最大值,每次先将满足条件的整个区间中的最优子区间加入优先队列,然后,再向两边扩展。取前k个】
【st表中除了记录最大值val外,还需记录最大值位置h,区间左端点left,右端点right,起点start,以便于从最优值位置分别向左右端点扩展】
#include<cmath>
#include<queue>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
struct node{
int left,right,start,h;
ll val;
bool operator <(const node &x)const
{
return x.val>val;
}
}st[500010][20];
priority_queue<node>que;
int mi[500010];
int n,m,L,R,a[500010],sum[500010];
ll ans;
inline void pre()
{
int i,j,t=1;
mi[1]=0;
for(i=2;i<=n;++i)
{
while((1<<t)<=i) ++t;
mi[i]=t-1;
}
for(i=1;i<=n;++i) st[i][0].val=sum[i],st[i][0].h=i;
for(j=1;j<20;++j)
for(i=1;i<=n;++i)
if(i+(1<<j)-1<=n)
if(st[i][j-1].val<st[i+(1<<(j-1))][j-1].val) st[i][j]=st[i+(1<<(j-1))][j-1];
else st[i][j]=st[i][j-1];
}
inline node find(int l,int r)
{
int k=mi[r-l+1];
if(st[l][k].val<st[r-(1<<k)+1][k].val) return st[r-(1<<k)+1][k];
return st[l][k];
}
inline void add(int l,int r,int head)
{
if(l>r) return;
node now=find(l,r);
now.val-=sum[head-1]; now.left=l; now.right=r; now.start=head;
que.push(now);
}
int main()
{
int i,j;
scanf("%d%d%d%d",&n,&m,&L,&R);
for(i=1;i<=n;++i)scanf("%d",&a[i]),sum[i]=sum[i-1]+a[i];
pre();
for(i=1;i<=n;++i)
{
if(i+L-1>n) continue;
int l=min(n,i+L-1),r=min(i+R-1,n);
add(l,r,i);
}
while(m--)
{
node now=que.top(); que.pop();
ans+=now.val;
add(now.left,now.h-1,now.start);
add(now.h+1,now.right,now.start);
}
printf("%lld\n",ans);
return 0;
}