【BZOJ2006】【NOI2010】超级钢琴

题意:

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

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

题解:

貌似是此类套路题的开端?

一看到前k大很明显就是要用大根堆(优先队列)来维护,取k次堆顶即可。

区间求和可以转化为前缀和差分,那么对于一个左端点i,能找到的最优的右端点j就是在区间$[i-1+L,i-1+R]$中$pre[j]$最大的j;

显然是RMQ问题,用ST表可以做到$O(1)$

用一个四元组$(i,t,l,r)$来表示一个超级和弦,表示左端点是i,右端点区间为$[l,r]$,最优右端点为t;

先加入初始的n个最优的和弦,每次取完堆顶把右端点区间以t为中心分裂成两个重新加入队列,统计答案即可。

注意最后的答案要用longlong

代码:

 1 #include<algorithm>
 2 #include<iostream>
 3 #include<cstring>
 4 #include<cstdio>
 5 #include<cmath>
 6 #include<queue>
 7 #define inf 2147483647
 8 #define eps 1e-9
 9 using namespace std;
10 typedef long long ll;
11 int n,k,l,r,a[500001],pre[500001],lg[500001],st[500001][20];
12 ll ans;
13 struct node{
14     int t1,t2,l,r;
15     friend bool operator <(node x,node y){
16         return pre[x.t2]-pre[x.t1-1]<pre[y.t2]-pre[y.t1-1];
17     }
18 };
19 priority_queue<node>q;
20 int query(int l,int r){
21     if(l==r)return l;
22     return (pre[st[l][lg[r-l+1]]]>pre[st[r-(1<<lg[r-l+1])+1][lg[r-l+1]]])?st[l][lg[r-l+1]]:st[r-(1<<lg[r-l+1])+1][lg[r-l+1]];
23 }
24 int main(){
25     scanf("%d%d%d%d",&n,&k,&l,&r);
26     lg[1]=0;
27     for(int i=2;i<=n;i++)lg[i]=lg[i>>1]+1;
28     for(int i=1;i<=n;i++)st[i][0]=i;
29     for(int i=1;i<=n;i++){
30         scanf("%d",&a[i]);
31         pre[i]=pre[i-1]+a[i];
32     }
33     for(int j=1;j<=19;j++){
34         for(int i=1;i+(1<<j)-1<=n;i++){
35             st[i][j]=(pre[st[i][j-1]]>pre[st[i+(1<<j-1)][j-1]])?st[i][j-1]:st[i+(1<<j-1)][j-1];
36         }
37     }
38     for(int i=1;i<=n;i++){
39         if(i+l-1<=n){
40             q.push((node){i,query(i+l-1,min(i+r-1,n)),i+l-1,min(i+r-1,n)});
41         }
42     }
43     for(int i=1;i<=k;i++){
44         node now=q.top();
45         q.pop();
46         ans+=pre[now.t2]-pre[now.t1-1];
47         if(now.t2-1>=now.l)q.push((node){now.t1,query(now.l,now.t2-1),now.l,now.t2-1});
48         if(now.t2+1<=now.r)q.push((node){now.t1,query(now.t2+1,now.r),now.t2+1,now.r});
49     }
50     printf("%lld",ans);
51     return 0;
52 }
posted @ 2018-10-21 09:38  DCDCBigBig  阅读(203)  评论(0编辑  收藏  举报