Solution
对于任意区间右端点 i,其左端点取值在 l, r之间,若左端点为 m,则 v为 max(sum[i]−sum[m−1]),显然这里 i是不变的,所以可以用 st表查询 m的位置,然后计算v。
现将所有右端点扫一遍,然后扔到堆里面,堆中节点记录的是决策,即右端点 i,左端点区间,优先级由 v决定
然后取出堆顶, v加到 ans里去,分裂 [l,r]为 [l,m−1]和 [m+1,r], RMQ出新的 v,再将分裂后的区间加到堆里
重复 k次
Code
#include<bits/stdc++.h>
using namespace std;
const int N=500002;
struct node{
int i,l,m,r,v;
friend bool operator <(node a,node b){return a.v<b.v;}
}t;
priority_queue<node>q;
int lg[N],L,R,i,j,n,k,a[N],tmp,st[N][20],l,r,m;
long long ans;
inline char gc(){
static char buf[100000],*p1=buf,*p2=buf;
return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline int rd(){
int x=0,fl=1;char ch=gc();
for (;ch<48||ch>57;ch=gc())if(ch=='-')fl=-1;
for (;48<=ch&&ch<=57;ch=gc())x=(x<<3)+(x<<1)+(ch^48);
return x*fl;
}
int get(int x,int y){
x--;y--;
int l=lg[y-x+1];
if (a[st[x][l]]<a[st[y-(1<<l)+1][l]]) return st[x][l]+1;
return st[y-(1<<l)+1][l]+1;
}
int main(){
n=rd();k=rd();L=rd();R=rd();
lg[0]=-1;
for (i=1;i<=n;i++) a[i]=rd()+a[i-1],lg[i]=lg[i>>1]+1,st[i][0]=i;
for (j=1;j<=lg[n];j++)
for (i=0;i+(1<<j)-1<=n;i++)
if (a[st[i][j-1]]<a[st[i+(1<<j-1)][j-1]]) st[i][j]=st[i][j-1];
else st[i][j]=st[i+(1<<j-1)][j-1];
for (i=1;i<=n;i++){
l=max(i-R+1,1);r=max(i-L+1,1);
if (i-l+1<L) continue;
m=get(l,r);
q.push((node){i,l,m,r,a[i]-a[m-1]});
}
while (k--){
t=q.top();q.pop();
ans+=t.v;
l=t.l;r=t.r;m=t.m;
if (l<m){
tmp=get(l,m-1);
q.push((node){t.i,l,tmp,m-1,a[t.i]-a[tmp-1]});
}
if (m<r){
tmp=get(m+1,r);
q.push((node){t.i,m+1,tmp,r,a[t.i]-a[tmp-1]});
}
}
printf("%lld",ans);
}