题意
好题好题
求k个区间使得和最大,要求区间的长度在L到R之间。
题解
非常有意思的一道题
考虑前缀和,那么以l为左端点的区间的和都是是s[i]-s[l-1]。
那么用RMQ来预处理出区间s[]的最大值。
可以想到取前k大的话,那应该是要用堆的。
堆中记录一个五元组(i,l,r,val,pos)表示左端点为i,右端点在[l,r]之间,最大价值为val,最大价值所对应的右端点为pos。
取出一个五元组(i,l,r,val,pos),将它分裂成两半,(i,l,pos-1,val’,pos’),(i,pos+1,r,val”,pos”)。
最后取k个就可以了(写得有些匆忙,如有疑问欢迎提出qwq)
//Suplex
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
#define N 500500
using namespace std;
int n,k,L,R,a[N],s[N],f[N][23];
long long ans;
struct leo{
int i,l,r,val,pos;
bool operator < (const leo & A) const {return val<A.val;}
};
priority_queue<leo>heap;
int MAX(int a,int b)
{
return s[a]>s[b] ? a : b;
}
int main()
{
scanf("%d%d%d%d",&n,&k,&L,&R);
for(int i=1;i<=n;i++) scanf("%d",&a[i]),s[i]=s[i-1]+a[i];
for(int i=1;i<=n;i++) f[i][0]=i;
for(int j=1;j<=(int)log2(n);j++)
for(int i=1;i<=n-(1<<j)+1;i++)
f[i][j]=MAX(f[i][j-1],f[i+(1<<(j-1))][j-1]);
for(int i=1;i<=n;i++)
if(i+L-1<=n){
int ls=i+L-1,rs=min(i+R-1,n),len=log2(rs-ls+1);
int pos=MAX(f[ls][len],f[rs-(1<<len)+1][len]);
int val=s[pos]-s[i-1];
heap.push((leo){i,ls,rs,val,pos});
}else break;
while(!heap.empty() && k){
leo now=heap.top();heap.pop();
ans+=now.val;k--;
leo ls=now,rs=now;
ls.r=now.pos-1;rs.l=now.pos+1;
if(ls.r>=ls.l){
int len=log2(ls.r-ls.l+1);
ls.pos=MAX(f[ls.l][len],f[ls.r-(1<<len)+1][len]);
heap.push((leo){now.i,ls.l,ls.r,s[ls.pos]-s[now.i-1],ls.pos});
}
if(rs.r>=rs.l){
int len=log2(rs.r-rs.l+1);
rs.pos=MAX(f[rs.l][len],f[rs.r-(1<<len)+1][len]);
heap.push((leo){now.i,rs.l,rs.r,s[rs.pos]-s[now.i-1],rs.pos});
}
}
printf("%lld\n",ans);
return 0;
}