题目链接
设$s_i=sumlimits_{j=1}^i a_j$,那么相同左端点$l$的区间之中,最大的区间和就是$mathop{max}limits_{i=l+L-1}^{i+R-1}{s_i-s_{l-1}}=mathop{max}limits_i{s_i}-s_{l-1}$。
那对前缀和做ST表就可以快速求解了。
设$f(i,l,r,t)$表示左端点是$i$,右端点在$[l,r]$范围内,使区间和最大的区间右端点是$t$,的最大区间和。
那我们可以把所有的$f(i,i+L-1,i+R-1,t)$丢进堆里,取$k$次,每次把右端点区间按$(l,t-1)$和$(t+1,r)$分开重新压进堆里。
代码(100分):
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<algorithm> #include<vector> #include<queue> #include<map> #include<set> #define IL inline #define RG register #define _1 first #define _2 second using namespace std; typedef long long LL; const int N=5e5; const int L=18; int n,k,llim,rlim; LL a[N+3],s[N+3]; int lg2[N+3]; int f[N+3][L+3]; IL void init(){ s[0]=0; for(int i=1;i<=n;i++) s[i]=s[i-1]+a[i]; lg2[0]=lg2[1]=0; for(int i=2;i<=n;i++) if((1<<(lg2[i-1]+1))==i) lg2[i]=lg2[i-1]+1; else lg2[i]=lg2[i-1]; for(int i=1;i<=n;i++) f[i][0]=i; for(int l=1;l<=lg2[n];l++) for(int i=1;i<=n;i++){ int j=i+(1<<(l-1)); if(j>n||s[f[i][l-1]]>s[f[j][l-1]]) f[i][l]=f[i][l-1]; else f[i][l]=f[j][l-1]; } } IL int qry(int l,int r){ int len=r-l+1,k=lg2[len],t=l+len-(1<<k); return (s[f[l][k]]>s[f[t][k]])?f[l][k]:f[t][k]; } struct Dat{ int i,l,r,k; Dat(){} Dat(int i,int l,int r,int k) :i(i),l(l),r(r),k(k){} }; IL bool operator<(Dat x,Dat y){ //warning: i differ return s[x.k]-s[x.i-1]<s[y.k]-s[y.i-1]; } priority_queue<Dat>hp; int main(){ scanf("%d%d%d%d",&n,&k,&llim,&rlim); for(int i=1;i<=n;i++) scanf("%lld",&a[i]); init(); for(int i=1;i+llim-1<=n;i++){ int l=i+llim-1,r=min(i+rlim-1,n); hp.push(Dat(i,l,r,qry(l,r))); } LL ans=0; while(k--){ Dat x=hp.top(); hp.pop(); ans+=s[x.k]-s[x.i-1]; if(x.l<x.k) hp.push(Dat(x.i,x.l,x.k-1,qry(x.l,x.k-1))); if(x.k<x.r) hp.push(Dat(x.i,x.k+1,x.r,qry(x.k+1,x.r))); } printf("%lld",ans); return 0; }