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
只有一个整数,表示乐曲美妙度的最大值。
Sample Input
4 3 2 3
3
2
-6
8
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。
【样例说明】
共有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。
题解Here!
题目大意:
有$n$个音符$A_1,A_2,A_3,...A_n$。
要求找到$k$段超级和弦组成的乐曲,每段连续的音符的个数$x$满足$L\leq x\leq R$,求乐曲美妙度的最大值。
首先可以看到,每段超级和弦都是连续的,美妙度是这段区间内所有美妙度的和。
可以想到,每次求解区间和显然是不合算的,所以考虑到用前缀和。
考虑暴力,我们需要把所有满足条件的字段抽出来排个序,但这实在是不可想象。
所以考虑使用贪心思想来解决这个问题。
首先,我们定义$f(s,l,r)=\max \{sum(t)-sum(s-1)\ |\ l\leq t\leq r\}$,即以$s$为左端点,右端点范围是$[l,r]$的最大子段和,$sum$是前缀和。
可以看出,$s$的位置是固定的。
所以$sum(s-1)$也是固定的。
所以我们要求这个的最大值,只要$sum(t)$最大就可以了。
即要求$sum(t)$在$t\in [l,r]$时的最大值。
这个最大值的求解就丢给了$RMQ$。。。
当然,具体计算的时候还要看看上界$r$是否超过了$n$。
接下来想怎么贪心。
我们可以每次都选最优的子段,这样选$k$次显然就是我们所要的结果。
那怎么找到最优解呢?
用堆来将解存进去,每次堆顶的元素就是最优解。
考虑一个三元组$(s,l,r)$表示以$s$为左端点,右端点的选择区间为$[l, r]$的情况。
我们假设当前最大的三元组是$(s,l,r)$,最优解位置是$t$。
由于$t$已经被选中,对于这个$s$,$t$已经不能重复选中,但最优解还可能存在于$t$左右的两端区间中。
所以提取出$(s,l,r)$之后,为了避免重复且不丧失其他较优解,我们仍然要把$(s,l,t-1),(o,t+1,r)$扔回堆里面去。
显然地,在放回去之前应该保证区间的存在,即$l=t$或$r=t$的情况要进行特判。
由此,我们查询区间最大值的时候查询的是最优解的位置。
然后就没有然后了。。。
附代码:
#include<iostream> #include<algorithm> #include<cstdio> #include<queue> #include<cmath> #define MAXN 500010 using namespace std; int n,m,l,r,f[MAXN][20]; long long sum[MAXN]; struct node{ int s,l,r,t; friend bool operator <(const node &p,const node &q){ return sum[p.t]-sum[p.s-1]<sum[q.t]-sum[q.s-1]; } }; priority_queue<node> q; inline int read(){ int date=0,w=1;char c=0; while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();} while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();} return date*w; } void step(){ for(int i=1;i<=n;i++)f[i][0]=i; for(int i=1;(1<<i)<=n;i++) for(int j=1;j+(1<<i)-1<=n;j++){ int x=f[j][i-1],y=f[j+(1<<(i-1))][i-1]; f[j][i]=(sum[x]>sum[y]?x:y); } } inline int query(int l,int r){ int k=log2(r-l+1); int x=f[l][k],y=f[r-(1<<k)+1][k]; return (sum[x]>sum[y]?x:y); } void work(){ long long ans=0; node u,v; while(m--){ u=q.top(); q.pop(); ans+=sum[u.t]-sum[u.s-1]; if(u.l!=u.t){ v=u; v.r=v.t-1; v.t=query(v.l,v.r); q.push(v); } if(u.r!=u.t){ v=u; v.l=v.t+1; v.t=query(v.l,v.r); q.push(v); } } printf("%lld\n",ans); } void init(){ n=read();m=read();l=read();r=read(); for(int i=1;i<=n;i++){ sum[0]=0; int x=read(); sum[i]=sum[i-1]+x; } step(); for(int i=1;i<=n;i++)if(i+l-1<=n)q.push((node){i,i+l-1,min(i+r-1,n),query(i+l-1,min(i+r-1,n))}); } int main(){ init(); work(); return 0; }