1. 程式人生 > >P2048 [NOI2010]超級鋼琴(RMQ)

P2048 [NOI2010]超級鋼琴(RMQ)

space etc color getchar() truct urn 區間 top isp

P2048 [NOI2010]超級鋼琴

區間和--->前綴和做差

多次查詢區間和最大--->前綴和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; }

P2048 [NOI2010]超級鋼琴(RMQ)