[NOI2010]超级钢琴 划分树+堆
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<queue> using namespace std; #define N 510000 struct P{ int x,y; P(int a=0,int b=0){x=a,y=b;} bool operator<(P a)const{ return y<a.y; } }; int val[N][23],num[N][23],ci[N]; int s[N]; int n,k,L,R; void build(int h,int L,int R){ if(L==R)return; int mid=(L+R)/2,left=L,right=mid+1,same=mid-L+1; for(int i=L;i<=R;i++)if(val[i][h]<s[mid])same--; for(int i=L;i<=R;i++){ if(val[i][h]<s[mid])val[left++][h+1]=val[i][h]; else if(val[i][h]==s[mid]&&same){ val[left++][h+1]=val[i][h]; --same; }else{ val[right++][h+1]=val[i][h]; } num[i][h]=num[L-1][h]+left-L; } build(h+1,L,mid); build(h+1,mid+1,R); } int kth(int h,int L,int R,int x,int y,int k){ if(L==R)return val[L][h]; int mid=(L+R)/2,left=num[y][h]-num[x-1][h]; int pre=num[x-1][h]-num[L-1][h]; if(left>=k)return kth(h+1,L,mid,L+pre,L+pre+left-1,k); else{ k-=left; pre=x-L-pre; left=y-x+1-left; return kth(h+1,mid+1,R,mid+pre+1,mid+pre+left,k); } } int Kth(int x,int y,int k){ return kth(1,1,n,x,y,k); } priority_queue<P> jj; int l[N],r[N]; long long ans; int main(){ scanf("%d%d%d%d",&n,&k,&L,&R); for(int i=1;i<=n;i++){ int a; scanf("%d",&a); s[i]=s[i-1]+a; } for(int i=1;i<=n;i++)val[i][1]=s[i]; sort(s+1,s+n+1); build(1,1,n); for(int i=1;i<=n-L+1;i++){ l[i]=i+L-1; r[i]=min(n,i+R-1); jj.push(P(i,Kth(l[i],r[i],r[i]-l[i]+1)-val[i-1][1])); } for(int i=1;i<=k;i++){ P x=jj.top(); jj.pop(); ans+=x.y; if(r[x.x]-l[x.x]>ci[x.x]){jj.push(P(x.x,Kth(l[x.x],r[x.x],r[x.x]-l[x.x]-ci[x.x])-val[x.x-1][1])); ci[x.x]++; } } cout<<ans; }