题意就不说了。
我一开始想的是把每一位看做最后的节点,然后二分这个点做为右端点的序列长度,用主席树随便维护一下。然而这个序列不是有序的,不满足二分性。。
我们用优先队列处理一下,同样是把每个端点看作右端点,然后在i-r-1到i-l这段区间里找K小,用优先队列记录一下,找到k个就结束了,时间复杂度klogn。
code(万年RE):
#include<bits/stdc++.h>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
#define L -500000005
#define R 500000005
using namespace std;
const int N=5e5+7;
typedef long long ll;
ll sum[N];
int a[N];
struct data
{
int num,k;
ll val;
bool operator <(const data&w)const
{
return val<w.val;
}
}t;
priority_queue<data>q;
namespace node
{
int root[N],tot;
struct ct
{
int l,r,s;
}t[20000005];
inline void ins(int &x,int y,ll l,ll r,ll pos)
{
x=++tot;
t[x].s=t[y].s+1;
if (l==r)return;
t[x].l=t[y].l;
t[x].r=t[y].r;
int mid=(l+r)>>1;
if (pos<=mid)ins(t[x].l,t[y].l,l,mid,pos);
else ins(t[x].r,t[y].r,mid+1,r,pos);
}
inline ll query(int x,int y,ll l,ll r,int k)
{
if (t[y].s-t[x].s<k)return 1e9;
if (l==r)return l;
int mid=(l+r)>>1;
int s=t[t[y].l].s-t[t[x].l].s;
if (k<=s)return query(t[x].l,t[y].l,l,mid,k);
else return query(t[x].r,t[y].r,mid+1,r,k-s);
}
}
int main()
{
int n,k,l,r;
scanf("%d%d%d%d",&n,&k,&l,&r);
ll ans=0;
node::ins(node::root[0],0,L,R,0);
fo(i,1,n)scanf("%d",&a[i]);
fo(i,1,n)sum[i]=sum[i-1]+a[i];
fo(i,1,n)node::ins(node::root[i],node::root[i-1],L,R,sum[i]);
fo(i,l,n)
{
int r1=i-l,l1=i-r-1;
ll tmp;
if (l1<0)tmp=node::query(0,node::root[r1],L,R,1);
else tmp=node::query(node::root[l1],node::root[r1],L,R,1);
q.push((data){i,1,sum[i]-tmp});
}
while (k--)
{
t=q.top();
q.pop();
ans+=t.val;
int r1=t.num-l,l1=t.num-r-1;
ll tmp;
if (l1<0)tmp=node::query(0,node::root[r1],L,R,t.k+1);
else tmp=node::query(node::root[l1],node::root[r1],L,R,t.k+1);
q.push((data){t.num,t.k+1,sum[t.num]-tmp});
}
printf("%lld\n",ans);
return 0;
}