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