【bzoj 2006】【codevs 2934】[NOI2010]超级钢琴(st表+优先队列)

47 篇文章 0 订阅
14 篇文章 0 订阅

2006: [NOI2010]超级钢琴

Time Limit: 20 Sec   Memory Limit: 552 MB
Submit: 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

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]

【题解】【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;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值