BZOJ2006 [NOI2010]超级钢琴

本文版权归ljh2000和博客园共有,欢迎转载,但须保留此声明,并给出原文链接,谢谢合作。

 

 

本文作者:ljh2000
作者博客:http://www.cnblogs.com/ljh2000-jump/
转载请注明出处,侵权必究,保留最终解释权!

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大的连续子段和的总和。我们考虑一个问题,固定右端点,然后可以很快得到以这个端点为右端点的区间内的总和最大的区间。ST表预处理一下即可。所以我们很快得到一个思路:对于每个端点,得到以它为右端点的最优情况,用堆维护,取出前k大的值。但现在存在一个问题:可能以某一个端点为右端点的区间不止一个在前k个中。既然题目要求k个不相同的区间,我们就考虑拆分一下我们上次做的区间。比如我上次的最优解是[p,i]那么这次为了与上次取得不同,新的区间的左端点就应该在[1,p-1]、[p+1,i]中产生,右端点仍然是i。这样做的话时间复杂度是O(k log n+n log n),可以接受。

   考试的时候我担心被卡常就写了手写堆,其实也不需要啦。

 

  1 //It is made by ljh2000
  2 #include <iostream>
  3 #include <cstdlib>
  4 #include <cstring>
  5 #include <cstdio>
  6 #include <cmath>
  7 #include <algorithm>
  8 #include <ctime>
  9 #include <vector>
 10 #include <queue>
 11 #include <map>
 12 #include <set>
 13 using namespace std;
 14 typedef long long LL;
 15 #define RG register
 16 const int inf = (1<<30);
 17 const int MAXN = 500011;
 18 int n,k,L,R,zuo,you,a[MAXN];
 19 LL sum[MAXN],psum[MAXN],ans;
 20 int ST[MAXN][20],belong[MAXN];
 21 int mi[20];
 22 int Q[MAXN*4],cnt,top;
 23 struct node{
 24     int left,right;
 25     int nowl,nowr;
 26     LL val;
 27 }jump[MAXN*4],tmp,tmp1,tmp2;
 28 inline int getint()
 29 {
 30     RG int w=0,q=0; RG char c=getchar();
 31     while((c<'0' || c>'9') && c!='-') c=getchar(); if(c=='-') q=1,c=getchar(); 
 32     while (c>='0' && c<='9') w=w*10+c-'0', c=getchar(); return q ? -w : w;
 33 }
 34 
 35 inline int query(RG int l,RG int r){
 36     RG int tt=belong[r-l+1];
 37     if(psum[ST[l][tt]]<psum[ST[r-mi[tt]+1][tt]]) return ST[l][tt];
 38     return ST[r-mi[tt]+1][tt];
 39 }
 40 
 41 inline void push(RG int x){
 42     Q[++top]=x;
 43     RG int nn=top,fa=top/2; if(fa==0) return ;
 44     while(fa>0) {
 45     if(jump[Q[fa]].val<jump[Q[nn]].val) swap(Q[fa],Q[nn]);
 46     else break;
 47     nn=fa; fa=nn/2;
 48     }
 49 }
 50 
 51 inline void pop(){
 52     Q[1]=Q[top]; top--; if(top==0) return ; 
 53     RG int nn=1,ll=nn*2,rr=ll+1; RG int zhi;
 54     while(ll<=top) {
 55     zhi=-1; 
 56     if(jump[Q[ll]].val>jump[Q[rr]].val) zhi=ll;
 57     else zhi=rr;
 58     if(jump[Q[zhi]].val>jump[Q[nn]].val) swap(Q[zhi],Q[nn]),nn=zhi;
 59     else break;
 60     ll=nn*2; rr=ll+1;
 61     }
 62 }
 63 
 64 inline void update(RG node qq){
 65     qq.nowl=query(qq.left,qq.right);
 66     qq.val=sum[qq.nowr]-psum[qq.nowl];
 67     jump[++cnt]=qq; 
 68     push(cnt);
 69 }
 70 
 71 inline void work(){
 72     n=getint(); k=getint(); L=getint(); R=getint(); for(RG int i=1;i<=n;i++) a[i]=getint(),sum[i]=sum[i-1]+a[i];
 73     for(RG int i=2;i<=n;i++) psum[i]=sum[i-1]; mi[0]=1; for(int i=1;i<=19;i++) mi[i]=mi[i-1]<<1;
 74     for(RG int i=1;i<=n;i++) ST[i][0]=i;
 75     for(RG int j=1;j<=19;j++)
 76     for(RG int i=1;i<=n;i++) {
 77         ST[i][j]=ST[i][j-1];
 78         if(i+mi[j-1]<=n && psum[ ST[i+mi[j-1]][j-1] ]<=psum[ST[i][j-1]])  ST[i][j]=ST[i+mi[j-1]][j-1];
 79     }
 80     belong[1]=0; for(RG int i=2;i<=n;i++) belong[i]=belong[i>>1]+1;
 81     for(RG int i=L;i<=n;i++){//考虑以i为右端点的情况
 82     zuo=i-R+1; you=i-L+1; if(zuo<1) zuo=1;
 83     jump[++cnt].nowr=i; jump[cnt].nowl=query(zuo,you);
 84     jump[cnt].left=zuo; jump[cnt].right=you;
 85     jump[cnt].val=sum[i]-psum[jump[cnt].nowl];
 86     push(cnt);
 87     }
 88     while(k>0) {//[left,right] --> [left,nowl-1] [nowl+1,right]拆分
 89     tmp1=tmp2=tmp=jump[Q[1]]; pop();     
 90     tmp1.right=tmp.nowl-1; tmp2.left=tmp.nowl+1;
 91     ans+=tmp.val; 
 92     if(tmp.left<=tmp1.right) update(tmp1);    
 93     if(tmp2.left<=tmp.right) update(tmp2);
 94     k--;
 95     }
 96     printf("%lld",ans);
 97 }
 98 
 99 int main()
100 {
101     work();
102     return 0;
103 }
原文地址:https://www.cnblogs.com/ljh2000-jump/p/5953725.html