bzoj2006[NOI2010]超级钢琴

bzoj2006[NOI2010]超级钢琴

题意:

超级钢琴可以弹奏出n个音符,编号为1至n。第i个音符的美妙度为Ai,其中Ai可正可负。一个“超级和弦”由若干个编号连续的音符组成,包含的音符个数不少于L且不多于R,其美妙度为包含的所有音符的美妙度之和。两个超级和弦被认为是相同的,当且仅当这两个超级和弦所包含的音符集合是相同的。现需创作一首由k个不同的超级和弦组成的乐曲。定义一首乐曲的美妙度为其所包含的所有超级和弦的美妙度之和。求能够创作出来的乐曲美妙度最大值是多少。

题解:

乍一看很难,实际上是可做的。题目实际上是求前k大的区间。设计状态(x,l,r,y,z)表示左端点为x,右端点在l到r之间的区间最大值为y且最大的区间右端点为z。预处理求出所有(i,i+L-1,i+R-1)的状态放入按y排的优先队列。枚举k次,每次从优先队列中取出最大的累加它的y,然后这个区间不能取了,所以求(x,l,z-1)和(x,z+1,r)再放入优先队列。怎么求这些状态?因为做端点是固定的,所以求区间s[l..r]的RMQ就行了,预处理时顺便求出f数组以便于求RMQ的时候用倍增。反思:我觉得RMQ的倍增和LCA的倍增是一样的,所以就用了求LCA的倍增写法,过倒是能过,速度不知道会不会慢一点。

代码:

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <algorithm>
 4 #include <queue>
 5 #define inc(i,j,k) for(int i=j;i<=k;i++)
 6 #define INF 0x3fffffff
 7 using namespace std;
 8 
 9 struct node{
10     int f,l,r,a1,a2;
11     bool operator < (const node&a)const{
12         return a1<a.a1;
13     }
14 };
15 int n,k,l,r,f1[600000][20],f2[600000][20],s[600000];
16 priority_queue <node> q;
17 void add(int f,int l,int r){
18     if(l==r){q.push((node){f,l,r,s[l]-s[f-1],l}); return;}
19     if(l>r)return;
20     int a1=r-l,a2=-INF,a3,a4=l;
21     for(int i=0;(1<<i)<=a1;i++)if(a1&(1<<i)){
22         if(f1[a4][i]>a2)a2=f1[a4][i],a3=f2[a4][i]; a4+=(1<<i);
23     }
24     q.push((node){f,l,r,a2-s[f-1],a3});
25 }
26 int main(){
27     scanf("%d%d%d%d",&n,&k,&l,&r);
28     s[0]=0; inc(i,1,n){int a; scanf("%d",&a); s[i]=s[i-1]+a;}
29     inc(i,1,n-1)f1[i][0]=max(s[i],s[i+1]),f2[i][0]=s[i]>s[i+1]?i:i+1;
30     for(int j=1;(1<<j)<=n;j++)inc(i,1,n)if(i+(1<<j)<=n){
31         f1[i][j]=max(f1[i][j-1],f1[i+(1<<(j-1))][j-1]); 
32         f2[i][j]=s[f2[i][j-1]]>s[f2[i+(1<<(j-1))][j-1]]?f2[i][j-1]:f2[i+(1<<(j-1))][j-1];
33     }
34     inc(i,1,n)add(i,i+l-1,min(i+r-1,n)); long long ans=0;
35     inc(i,1,k){
36         node now=q.top(); q.pop(); ans=ans+(long long)now.a1; add(now.f,now.l,now.a2-1); add(now.f,now.a2+1,now.r);
37     }
38     printf("%lld",ans);
39     return 0;
40 }

 

20160325

posted @ 2016-07-09 23:05  YuanZiming  阅读(310)  评论(2编辑  收藏  举报