【BZOJ】2006: [NOI2010]超级钢琴(前缀和+RMQ+堆)
题目
传送门:QWQ
分析
又不会做。。。。。。。
显然很好想到前缀和处理一下。
然后考虑最大化结果,直接上st表。
问题来了,然后呢?
怎么做$ length \in [l,r] $ 呢?
正解是设一个五元组 (i,l,r,val,pos) 。
i是左端点,l,r是右端点范围,val是 i 到 pos的和, pos是 右端点位置。
然后对于 l,r 二分,扔进优先队列处理 。取出前 k 大,累加一下就是答案。
完了。
代码
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int maxn=500005; int n,posi[maxn][23]; int sum[maxn],Log2[maxn]; struct Node{ int i,l,r,val,pos; bool operator < (const Node& a) const{ return val<a.val;} }; inline int Max(int x,int y) {return sum[x]>sum[y]? x:y;} void ST(){ Log2[0]=-1; for(int i=1;i<=n;i++) if((i&(i-1))==0) Log2[i]=Log2[i-1]+1; else Log2[i]=Log2[i-1]; for(int i=1;i<=n;i++) posi[i][0]=i; for(int j=1;(1<<j)<=n;j++) for(int i=1;i+(1<<j)<=n+1;i++){ posi[i][j]=Max(posi[i][j-1],posi[i+(1<<(j-1))][j-1]); } } inline int RMQ(int l,int r){ int tmp=Log2[r-l+1]; return Max(posi[l][tmp],posi[r-(1<<tmp)+1][tmp]); } inline int read() { int x=0,f=1; char ch=getchar(); while (ch<'0' || ch>'9') {if (ch=='-') f=-1; ch=getchar();} while (ch>='0' && ch<='9') {x=x*10+ch-'0'; ch=getchar();} return x*f; } priority_queue<Node> que; int main() { int k,L,R; scanf("%d%d%d%d",&n,&k,&L,&R); for(int i=1;i<=n;i++){ sum[i]=sum[i-1]+read(); } ST(); for(int i=1;i<=n+1-L;i++){ int pos=RMQ(i+L-1,min(n,i+R-1)); // printf("---------- %d %d %d\n",i+L-1,min(n,i+R-1),pos); que.push((Node){i,i+L-1,min(n,i+R-1),sum[pos]-sum[i-1],pos}); } ll ans=0; while(k){ Node x=que.top();que.pop(); // printf("########## %d %d %d\n",x.i,x.pos,x.val); k--; ans+=(ll)x.val; Node ls=x,rs=x; ls.r=x.pos-1; rs.l=x.pos+1; if(ls.r>=ls.l) { ls.pos=RMQ(ls.l,ls.r); ls.val=sum[ls.pos]-sum[ls.i-1]; que.push(ls); } if(rs.r>=rs.l) { rs.pos=RMQ(rs.l,rs.r); rs.val=sum[rs.pos]-sum[rs.i-1]; que.push(rs); } } // puts("-----------debug------------"); // for(int i=1;i<=n;i++) printf("%d ",Log2[i]); printf("%lld\n",ans); return 0; }