题目大意:有n个数,从中找出权值和前k大的不相同的长度在[L,R]之间的连续子序列(1<=n,k<=500000).
写一下自己想到的做法.
设前缀和为s[i],则所求连续子序列可以表示为以下若干行:
-s[0]+s[L],-s[0]+s[L+1],…,-s[0]+s[R]
-s[1]+s[L+1],-s[1]+s[L+2],…,-s[1]+s[R+1]
…
-s[i]+s[L+i+1],-s[i]+s[L+i+2],…,-s[i]+s[R+i]
…
然后转化成经典问题:n个递增序列,从每个序列中选择一个数然后求和,求前k大的和(要求选法不同),这个用堆可以解决.
把上面的每行看做一个序列,序列不递增,所以我们还需要求区间k大.
所以这题堆+可持久化线段树就能A掉了(代码有些无法直视囧).
AC code:
#include <cstdio>
#include <queue>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N=500010;
const int LLIM=-1000000000;
const int RLIM=-LLIM;
int n,k,l,r,cnt;
int s[N],rk[N];
ll ans;
struct Data{
int x,v;
Data(int x,int v):x(x),v(v) {}
friend bool operator<(Data a,Data b){
return a.v<b.v;
}
};
priority_queue<Data> Q;
struct Tnod{
int le,ri,tot;
Tnod *lc,*rc;
}pool[N*35];
struct Segtree{
Tnod *root[N];
Segtree(){
for(int i=1;i<=n;i++) insert(&root[i],root[i-1],s[i],LLIM,RLIM);
}
void insert(Tnod **p,Tnod *q,int val,int L,int R){
*p=&pool[cnt++];(*p)->le=L;(*p)->ri=R;
if(L==R){
if(q==NULL) (*p)->tot=1;
else (*p)->tot=q->tot+1;
(*p)->lc=(*p)->rc=NULL;
return ;
}
int M=(L+R)>>1;
if(val<=M){
if(q!=NULL){
insert(&(*p)->lc,q->lc,val,L,M);
(*p)->rc=q->rc;
}
else{
insert(&(*p)->lc,NULL,val,L,M);
(*p)->rc=NULL;
}
}
else{
if(q!=NULL){
insert(&(*p)->rc,q->rc,val,M+1,R);
(*p)->lc=q->lc;
}
else{
insert(&(*p)->rc,NULL,val,M+1,R);
(*p)->lc=NULL;
}
}
if((*p)->lc==NULL) (*p)->tot=(*p)->rc->tot;
else if((*p)->rc==NULL) (*p)->tot=(*p)->lc->tot;
else (*p)->tot=(*p)->lc->tot+(*p)->rc->tot;
}
int kth(int L,int R,int rank){
return findkth(root[L-1],root[R],rank);
}
int findkth(Tnod *q,Tnod *p,int rank){
if(p->le==p->ri) return p->le;
if(p->lc==NULL){
if(q!=NULL) return findkth(q->rc,p->rc,rank);
else return findkth(NULL,p->rc,rank);
}
int ltot=p->lc->tot;
if(q!=NULL&&q->lc!=NULL) ltot-=q->lc->tot;
if(ltot>=rank){
if(q!=NULL) return findkth(q->lc,p->lc,rank);
else return findkth(NULL,p->lc,rank);
}
else{
if(q!=NULL) return findkth(q->rc,p->rc,rank-ltot);
else return findkth(NULL,p->rc,rank-ltot);
}
}
};
int main(){
scanf("%d%d%d%d",&n,&k,&l,&r);
for(int i=1;i<=n;i++){
int t;
scanf("%d",&t);
s[i]=s[i-1]+t;
}
for(int i=0;i<=n-l;i++) rk[i]=2;
Segtree T;
for(int i=0;i<=n-l;i++){
int L=i+l,R=min(i+r,n);
Q.push(Data(i,-s[i]+T.kth(L,R,R-L+1)));
}
for(int i=1;i<=k;i++){
Data d=Q.top();
Q.pop();
ans+=d.v;
if(rk[d.x]<=min(r-l+1,n-d.x-l+1)){
int L=d.x+l,R=min(d.x+r,n);
Q.push(Data(d.x,-s[d.x]+T.kth(L,R,R-L-rk[d.x]+2)));
rk[d.x]++;
}
}
printf("%lld\n",ans);
return 0;
}