【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
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。
题解:一开始想用主席树,然后越想越觉得做麻烦了,于是看题解发现用ST表就行。
我们先考虑较暴力的写法(最暴力的肯定是n2logn),假如我们已经确定了所选区间的右端点,那么我们能否快速知道最优的左端点是哪个呢?显然可以,我们将区间和转变为前缀相减的形式,求[l,r]的最大值也就是求s[r]-s[l-1]的最大值,因为r确定,而l只能在一段固定的区间,我们可以用ST表快速查询最小值。然后我们对于每个可行的右端点都找出最优的左端点,把它们扔到优先队列里一个一个取出来就行了。
但是问题来了,加入我们取出了点x,它的最优最短点y,那么在我们取出了y后,以后就不能再取y这个点了,那么我们该怎样将y删除呢?一个naive的想法就是用主席树,但是这要麻烦不少。
我们的目的就是想办法避免删除操作(因为ST表是不支持修改的),我们发现,加入原来x的左端点可以在[a,b]中选择,我们与其从[a,b]中去掉y,不如将[a,b]分裂成[a,y-1]和[y+1,b]两段,然后将这两段都扔到优先队列中。也就是说,我们在优先队列中存放的其实是一个四元组(sum,x,a,b),分别代表区间和,右端点,合法左端点的区间最左边和最右边。
代码真的巨短。
#include <cstdio> #include <iostream> #include <queue> #include <cstring> #include <utility> #define mp(A,B,C,D) make_pair(make_pair(A,B),make_pair(C,D)) using namespace std; typedef pair<int,int> pii; priority_queue<pair<pii,pii> > pq; int n,m,L,R; long long ans; const int maxn=500010; int sn[maxn][20],v[maxn],s[maxn],Log[maxn]; int rd() { int ret=0,f=1; char gc=getchar(); while(gc<‘0‘||gc>‘9‘) {if(gc==‘-‘)f=-f; gc=getchar();} while(gc>=‘0‘&&gc<=‘9‘) ret=ret*10+gc-‘0‘,gc=getchar(); return ret*f; } int mn(int a,int b) { return s[a]<s[b]?a:b; } int query(int a,int b) { if(a>b) return -1; int k=Log[b-a+1]; return mn(sn[a][k],sn[b-(1<<k)+1][k]); } int main() { n=rd(),m=rd(),L=rd(),R=rd(); int i,j,x,y,a,b,c,d; for(i=2;i<=n;i++) Log[i]=Log[i>>1]+1; for(i=1;i<=n;i++) sn[i][0]=i,v[i]=rd(),s[i]=v[i]+s[i-1]; for(j=1;(1<<j)<=n;j++) for(i=0;i+(1<<j)-1<=n;i++) sn[i][j]=mn(sn[i][j-1],sn[i+(1<<j-1)][j-1]); for(i=L;i<=n;i++) pq.push(mp(s[i]-s[query(max(i-R,0),i-L)],i,max(i-R,0),i-L)); for(i=1;i<=m;i++) { pii t1=pq.top().first,t2=pq.top().second; ans+=t1.first,x=t1.second,a=t2.first,b=t2.second,y=query(a,b),pq.pop(); c=query(a,y-1),d=query(y+1,b); if(c!=-1) pq.push(mp(s[x]-s[c],x,a,y-1)); if(d!=-1) pq.push(mp(s[x]-s[d],x,y+1,b)); } printf("%lld",ans); return 0; }