区间长度是动的,考虑定一个右端点,那么左端点可以动。
对于每一个右端点,通过左端点移动,在给定长度范围[L,R]间,可以对应出一个最优解。
那么次优解,次次优解...怎么办呢。。
我们可以把左端点对应的范围[a,b]给分解成[a,pos-1],[pos+1,b]。其中pos为当前右端点对应最优解的位置。
这时候右端点还是刚才的右端点,而左端点的区间分成两半。那么次优解就在这两半中。
具体实现如下:
用ST表求前缀和最小值。那么对于一个位置i来说,它左端点的范围是[i-R+1,i-L+1]。
如果位置i的前缀和是sum[i],那么它对应的最优解就是(sum[i]-[i-R,i-L]中的前缀和最小值)
分解怎么办呢?
用一个优先队列,里面的一个元素记录四条信息:最优解的大小,左端点的左边界,左端点的右边界,右端点的位置。
这样我们就可以把元素分裂成两个丢进去了。
最大值一定在第一波里面(就是还没有元素还没有分裂的时候)。因为只有长度限制,没有端点不能取的限制。
第一波元素进队列后,最优解是从大到小排的。那么最大值一定是top。
次大值一定对应(top元素分出来的两个之一)或者(第二个元素,也就是当前次大元素)。
次大值不可能在top元素的后面的元素分裂出来的元素中,因为每个元素装的是最优解,并且最优解是从大到小排的!
如果后面的元素分裂后得出来的最优解比当前top元素对应的最优解都还要大,说明后面的这个元素分裂前对应的那个最优解都已经比top元素大了,这与从大到小排序矛盾。
那么我们把当前的top元素取出来,分裂,丢进去,下次再进来取,就一定取的是当前最大值,以此类推。。
几个注意事项:
输入有负数,读优带上负号,ST表存的是位置,要开long long。注意<符号的重载
注意区间范围,还有端点加一减一啥的。写的时候要想好各个参数的具体含义!
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=5e5+10;
const int mx=20;
int st[maxn][mx];
int n,k,L,R;
ll presum[maxn],ans=0;
struct elements{
ll sum;
int lpos,rpos,R;
elements(){}
elements(int a,int b,int c,int d):sum(a),lpos(b),rpos(c),R(d){}
friend inline bool operator<(const elements &a,const elements &b){return a.sum<b.sum;}
}a[maxn];
priority_queue<elements> Q;
inline int read(){
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1; ch=getchar();}
while(isdigit(ch)) x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
return x*f;
}
inline int MIN(int a,int b){return (presum[a]<presum[b])?(a):(b);}
inline int query(int l,int r){
if(l>r) return -1;
int k=log2(r-l+1);
return MIN(st[r-(1<<k)+1][k],st[l][k]);
}
int main(){
n=read(),k=read(),L=read(),R=read();
for(int i=1;i<=n;++i) presum[i]=presum[i-1]+read(),st[i][0]=i;
for(int i=1;(1<<i)<=n;++i)
for(int j=0;((1<<i)+j-1)<=n;++j)
st[j][i]=MIN(st[j][i-1],st[j+(1<<(i-1))][i-1]);
for(int i=L;i<=n;++i){
int now_pos=query(max(0,i-R),i-L);
Q.push(elements(presum[i]-presum[now_pos],max(i-R,0),i-L,i));
}
for(int i=1;i<=k;++i){
elements N=Q.top();
Q.pop(),ans+=N.sum;
int now_pos=query(N.lpos,N.rpos);
int d1=query(N.lpos,now_pos-1);
int d2=query(now_pos+1,N.rpos);
if(d1!=-1) Q.push(elements(presum[N.R]-presum[d1],N.lpos,now_pos-1,N.R));
if(d2!=-1) Q.push(elements(presum[N.R]-presum[d2],now_pos+1,N.rpos,N.R));
}
cout<<ans;
}