BZOJ2006[NOI2010]超级钢琴 ST表+堆
Posted CQzhangyu
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了BZOJ2006[NOI2010]超级钢琴 ST表+堆相关的知识,希望对你有一定的参考价值。
【BZOJ2006】[NOI2010]超级钢琴
Description
Input
Output
只有一个整数,表示乐曲美妙度的最大值。
Sample Input
3
2
-6
8
Sample Output
【样例说明】
共有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; }
以上是关于BZOJ2006[NOI2010]超级钢琴 ST表+堆的主要内容,如果未能解决你的问题,请参考以下文章
[BZOJ 2006][NOI2010]超级钢琴(ST表+堆)
bzoj 2006 [NOI2010]超级钢琴 堆 + ST表