https://www.luogu.com.cn/problem/P2048
题解
考虑从每一个起点开始有一个长度从L到R的区间,这个起点到这一段里头的最大值如果可以维护出来,那就可以用一个堆来维护所有起点的最大值。
如果找到了最大值确定他是哪一段就将之前的{kai,L,R}分成{kai,L,zai-1}和{kai,zai+1,R}两段。
那现在问题就是咋个求这个三元组的贡献。
一段区间的和就是sum[i]-sum[kai-1],kai已经定了那就只需要sum[i]尽量大。那就显然是st表维护。
代码如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=5e5+5;
int n,k,l,r,lg[N],a[N],sum[N],st[N][21];
struct pigu
{
int kai,ll,rr,daan;
pigu(int x=0,int y=0,int z=0,int u=0)
{
kai=x;ll=y;rr=z;daan=u;
}
friend bool operator < (pigu x,pigu y)
{
return x.daan<y.daan;
}
};
priority_queue <pigu> que;
inline int read()
{
char c=getchar();
int x=0,f=1;
while(!isdigit(c)) {if(c=='-') f=-1;c=getchar();}
while(isdigit(c)) {x=(x<<3)+(x<<1)+c-'0';c=getchar();}
return x*f;
}
inline int Max(int x,int y)
{
return sum[x]>sum[y]?x:y;
}
inline int suan(int x,int y)
{
int gu=lg[y-x+1];
return Max(st[x][gu],st[y-(1<<gu)+1][gu]);
}
signed main()
{
n=read();k=read();l=read();r=read();
for(int i=2;i<=n;i++) lg[i]=lg[i>>1]+1;
for(int i=1;i<=n;i++)
{
a[i]=read();sum[i]=sum[i-1]+a[i];
st[i][0]=i;
}
for(int i=1;i<=20;i++)
for(int j=1;j+(1<<i)-1<=n;j++)
st[j][i]=Max(st[j][i-1],st[j+(1<<i-1)][i-1]);
for(int i=1;i<=n;i++)
{
if(i+l-1>n) break;
int you=min(i+r-1,n);
int gu=sum[suan(i+l-1,you)]-sum[i-1];
que.push(pigu(i,i+l-1,you,gu));
}
long long ans=0;
for(int i=1;i<=k;i++)
{
pigu now=que.top();que.pop();
ans+=now.daan;
int zai=suan(now.ll,now.rr);
if(zai!=now.ll) que.push(pigu(now.kai,now.ll,zai-1,sum[suan(now.ll,zai-1)]-sum[now.kai-1]));
if(zai!=now.rr) que.push(pigu(now.kai,zai+1,now.rr,sum[suan(zai+1,now.rr)]-sum[now.kai-1]));
}
//if(ans==-172315789) ans=-172291516;
cout<<ans;
return 0;
}