bzoj 2006: [NOI2010]超级钢琴

bzoj 2006: [NOI2010]超级钢琴

Time Limit: 20 Sec  Memory Limit: 512 MB
Submit: 4491  Solved: 2312
[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。

 

题目大意:

求前k大的长度在L~R之间的最大连续子序列和的和。

 

题解:

首先先做一个前缀和,设sum[i]表示的是1~i的和

然后枚举右端点 r,区间所对应的左端点只能在 r - R + 1到 r - L + 1 之间,然后对sum数组的r - R 到 r - L 之间取个最小值就可以了,这里直接用st表维护,然后把这个最大的加到大根堆里,维护一个四元组(a, b, c, d),a表示答案,就是以b为右端点,左端点在c~d之间的最大连续子序列和。

然后每次把大根堆堆顶的元素拿出来,把答案加上a然后把这个区间拆成两个区间,假设最大的区间的左端点在mid这个位置,那么这个区间就可以拆成c~mid-1和mid+1~d这两个区间,再把它们加入到堆里,这样做k次就可以了。

code:(好像还比较短)

#include<bits/stdc++.h>
#define int long long
#define N 1000005
#define pr pair<int,int>//毒瘤操作
#define mp(a, b, c, d) make_pair(make_pair(a, b), make_pair(c, d))//毒瘤操作+1
using namespace std;
priority_queue<pair<pr, pr> >q;//毒瘤操作,这样就可以表示四元组了
int sum[N], f[N][24], n, k, L, R;
int mi(int x, int y){//比较x,y这两个位置的sum的值
	if(sum[x] < sum[y]) return x;
	return y;
}
int query(int x, int y){//求x~y的最小值的位置
	if(x > y) return -1;
	int k = log2(y - x + 1);
	return mi(f[x][k], f[y - (1 << k) + 1][k]);
}
signed main(){
	scanf("%lld%lld%lld%lld", &n ,&k, &L, &R);
	for(int i = 1; i <= n; i ++) scanf("%lld", &sum[i]), f[i][0] = i, sum[i] += sum[i - 1];
	for(int j = 1; j <= 20; j ++)
		for(int i = 0; i <= n; i ++)
			f[i][j] = mi(f[i][j - 1], (i + (1 << (j - 1)) <= n)? f[i + (1 << (j - 1))][j - 1]:f[i][j - 1]);//st表
	for(int i = L; i <= n; i ++) q.push(mp(sum[i] - sum[query(max(i - R, 0ll), i - L)], i, max(i - R, 0ll), i - L));//先把一开始的加进去
	int ans = 0;
	for(int i = 1; i <= k; i ++){
		pr h1 = q.top().first, h2 = q.top().second; q.pop();
		int a = h1.first, b = h1.second, c = h2.first, d = h2.second;
		ans += a; int mid = query(c, d);
		int x = query(c, mid - 1), y = query(mid + 1, d);//x,y分别为拆开后两个区间的最优左端点
		if(x != -1) q.push(mp(sum[b] - sum[x], b, c, mid - 1));//拆成两个区间
		if(y != -1) q.push(mp(sum[b] - sum[y], b, mid + 1, d));	
	}
	printf("%lld", ans);
	return 0;
}//好棒的一道题!

 

1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值