P2048 [NOI2010]超级钢琴(RMQ+堆+贪心)
区间和--->前缀和做差
多次查询区间和最大--->前缀和RMQ
每次取出最大的区间和--->堆
于是我们设个3元组$(o,l,r)$,表示左端点为$o$,右端点在$l,r$之间(最优处为$t$)的最大区间和。
$t$可以RMQ在$l,r$间$O(1)$查询
所以我们事先把$n$个三元组(1<=o<=n)扔到堆里,每次把$s[t]-s[o-1]$最大的拿出来累加进答案。
取出来后$[o,t]$就不能取了,于是我们再把$(o,l,t-1)$和$(o,t+1,r)$再扔进堆里就好辣
#include<iostream> #include<cstdio> #include<cstring> #include<queue> #define rint register int using namespace std; inline int Min(int a,int b){return a<b?a:b;} void read(int &x){ char c=getchar();x=0; bool f=1; while(c<'0'||c>'9') f=f&&(c!='-'),c=getchar(); while('0'<=c&&c<='9') x=x*10+(c^48),c=getchar(); x=f?x:-x; } #define N 500005 int n,k,s[N],f[19][N],Log[N],a,L,R; long long ans; void maketb(){//RMQ:f数组存的是最大值的下标 for(rint i=1;i<=n;++i) f[0][i]=i; for(rint j=1;(1<<j)<=n;++j) for(rint i=1;i+(1<<(j-1))<=n;++i){ if(s[f[j-1][i]]>s[f[j-1][i+(1<<(j-1))]]) f[j][i]=f[j-1][i]; else f[j][i]=f[j-1][i+(1<<(j-1))]; } } int ask(int l,int r){ int k=Log[r-l+1]; if(s[f[k][l]]>s[f[k][r-(1<<k)+1]]) return f[k][l]; else return f[k][r-(1<<k)+1]; } struct data{ int o,l,r,t; data(int A,int B,int C): o(A),l(B),r(C),t(ask(B,C)) {} bool operator < (const data &tmp) const{ return s[t]-s[o-1]<s[tmp.t]-s[tmp.o-1]; } };priority_queue <data> h; int main(){ read(n);read(k);read(L);read(R); Log[0]=-1; for(rint i=1;i<=n;++i) read(a),s[i]=s[i-1]+a,Log[i]=Log[i>>1]+1; maketb(); for(rint i=1;i+L-1<=n;++i) h.push(data(i,i+L-1,Min(i+R-1,n))); while(k--){ data x=h.top(); h.pop(); ans+=s[x.t]-s[x.o-1]; if(x.l<x.t) h.push(data(x.o,x.l,x.t-1)); if(x.r>x.t) h.push(data(x.o,x.t+1,x.r)); }printf("%lld",ans); return 0; }