超级钢琴题解

将序列之和转化为\(sum[r]-sum[l-1]\),固定右端点,左端取最大值
用堆丢出一个丢进两个就行了,

#include<bits/stdc++.h>
using namespace std;
const int N=5e5+6,M=20;
int n,k,L,R,t,mi[M],lg[N],f[N][M],g[N][M],sum[N];
long long ans=0;
struct xd{
    int l,r,x,y;
    bool operator < (const xd &a) const {return sum[x]-sum[y-1]<sum[a.x]-sum[a.y-1];}
}tmp,nw;
priority_queue<xd> q;
inline int read(){
    int T=0,F=1; char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-') F=-1; ch=getchar();}
    while(ch>='0'&&ch<='9') T=(T<<3)+(T<<1)+(ch-48),ch=getchar();
    return F*T;
}
void st(int l,int r,xd &p){
    int h=lg[r-l+1]; p.l=l,p.r=r;
    p.x=(f[l][h]>f[r-mi[h]+1][h]?g[l][h]:g[r-mi[h]+1][h]);
}
int main(){
    n=read(),k=read(),L=read(),R=read(),mi[0]=1,lg[0]=-1;
    for(int i=1;i<=n;++i) t=read(),sum[i]=sum[i-1]+t,f[i][0]=sum[i],g[i][0]=i,lg[i]=lg[i>>1]+1;
    for(int i=1;i<=19;++i) mi[i]=mi[i-1]*2;
    for(int i=1;i<=19;++i) for(int j=1;j+mi[i]-1<=n;++j) f[j][i]=max(f[j][i-1],f[j+mi[i-1]][i-1]),g[j][i]=(f[j][i-1]>f[j+mi[i-1]][i-1]?g[j][i-1]:g[j+mi[i-1]][i-1]);
    for(int i=1;i<=n;++i) if(i+L-1<=n) st(i+L-1,min(i+R-1,n),tmp),tmp.y=i,q.push(tmp);
    while(!q.empty()&&k--){
        tmp=q.top(),q.pop(),ans+=1ll*(sum[tmp.x]-sum[tmp.y-1]);
        if(tmp.l<=tmp.x-1) nw.y=tmp.y,st(tmp.l,tmp.x-1,nw),q.push(nw);
        if(tmp.r>=tmp.x+1) nw.y=tmp.y,st(tmp.x+1,tmp.r,nw),q.push(nw);
    }
    printf("%lld\n",ans);
    return 0;
} 
02-13 03:08