【题解】
(划分树做法)
要求出长度为l~r的前k大连续和,可以转化为k次求第i大连续和,这与划分树的作用有关先把前缀和预处理出来。一段连续和为 S(j)-S(i),固定i之后为 S(i+len)-S(i-1) (i为起点,len为长度,len∈[l,r])
这里面,若i一定,len就在固定区间内变动,连续和的k大值对应S(i+len)的k大值,问题就转化为"求给定区间的k大值",就可以用划分树了
但是起点有多个,而题目中"k次求第i大连续和"没办法真的快速做到一次求一个
可以先求出对于每个起点的区间内最大连续和来预备着,把它们存到堆里,每次询问时取出堆顶并放入这个起点对应的次大和即可
因此题目转化为:求 S(i+l-1)-s(i-1),S(i+l)-s(i-1),…,S(i+r-1)-s(i-1) (i∈[1,n-l+1])中前k大的值
Ⅰ首先,建一个大根堆。对于每个i,求出max{ S(i+l-1),S(i+l),…,S(i+r-1) }-s(i-1)加入堆中
Ⅱ然后,对于k次询问,每次取出堆顶,看这个堆顶是由哪个i得到的,再把这个i对应的{S(i+l-1),S(i+l),…,S(i+r-1)}-s(i-1)的次大值加入堆
时间复杂度:划分树预处理_O( n*log(n) ) +Ⅰ_O( n*log(n) ) +Ⅱ_O( k*log(n) )
【代码】
#include<stdio.h>
#include<stdlib.h>
typedef long long LL;
struct node
{
int v[500005],num[500005];
};
node T[20];
int s[500005]={0},p[500005]={0},heap[500005]={0},pos[500005]={0};
int ph=0;
int min(int a,int b)
{
if(a<b) return a;
return b;
}
void jh(int* a,int* b)
{
int t=*a;
*a=*b;
*b=t;
}
void jh_heap(int a,int b)
{
jh(&heap[a],&heap[b]);
jh(&pos[a],&pos[b]);
}
void kp(int low,int high)
{
int i=low,j=high,mid=s[(i+j)/2];
while(i<j)
{
while(s[i]<mid) i++;
while(s[j]>mid) j--;
if(i<=j)
{
jh(&s[i],&s[j]);
i++;
j--;
}
}
if(j>low) kp(low,j);
if(i<high) kp(i,high);
}
void build(int left,int right,int deep)
{
int mid=(left+right)/2,ln=left,rn=mid+1,isame=mid-left+1,same=0,i;
if(left==right) return;
for(i=left;i<=right;i++)
if(T[deep].v[i]<s[mid]) isame--;
for(i=left;i<=right;i++)
{
if(i==left) T[deep].num[i]=0;
else T[deep].num[i]=T[deep].num[i-1];
if(T[deep].v[i]<s[mid])
{
T[deep+1].v[ln++]=T[deep].v[i];
T[deep].num[i]++;
}
if(T[deep].v[i]>s[mid]) T[deep+1].v[rn++]=T[deep].v[i];
if(T[deep].v[i]==s[mid])
{
same++;
if(isame>=same)
{
T[deep+1].v[ln++]=T[deep].v[i];
T[deep].num[i]++;
}
else T[deep+1].v[rn++]=T[deep].v[i];
}
}
build(left,mid,deep+1);
build(mid+1,right,deep+1);
}
int cx(int x,int y,int k,int left,int right,int deep)
{
int mid=(left+right)/2,before,have,b,h,tx,ty,fk=y-x+1+1-k;
if(left==right) return T[deep].v[left];
if(x==left)
{
before=0;
have=T[deep].num[y];
}
else
{
before=T[deep].num[x-1];
have=T[deep].num[y]-T[deep].num[x-1];
}
if(fk<=have)//左
{
tx=left+before;
ty=left+before+have-1;
k=ty-tx+1+1-fk;
return cx(tx,ty,k,left,mid,deep+1);
}
else//右
{
b=x-1-left-before+1;
h=y-x+1-have;
tx=mid+1+b;
ty=mid+1+b+h-1;
k=ty-tx+1+1-(fk-have);
return cx(tx,ty,k,mid+1,right,deep+1);
}
}
void tj(int x,int t)
{
int i;
heap[++ph]=t;
pos[ph]=x;
for(i=ph;i!=1;i/=2)
{
if(heap[i]>heap[i/2]) jh_heap(i,i/2);
else return;
}
}
void sc()
{
int i=1;
jh_heap(1,ph);
ph--;
while(i*2<=ph)
{
i*=2;
if(i+1<=ph&&heap[i+1]>heap[i]) i++;
if(heap[i]>heap[i/2]) jh_heap(i,i/2);
else return;
}
}
int main()
{
LL ans=0;
int n,k,l,r,i,x;
scanf("%d%d%d%d",&n,&k,&l,&r);
for(i=1;i<=n;i++)
{
scanf("%d",&s[i]);
s[i]+=s[i-1];
T[0].v[i]=s[i];
}
T[0].v[0]=0;
kp(1,n);
build(1,n,0);
for(i=1;i<=n-l+1;i++)
{
p[i]=1;
tj(i,cx(i+l-1,min(n,i+r-1),1,1,n,0)-T[0].v[i-1]);
}
for(i=1;i<=k;i++)
{
ans+=(LL)heap[1];
x=pos[1];
sc();
p[x]++;
if((x+l-1)+p[x]-1<=min(n,x+r-1)) tj(x,cx(x+l-1,min(n,x+r-1),p[x],1,n,0)-T[0].v[x-1]);
}
printf("%lld",ans);
return 0;
}