[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;
}

 

posted @ 2014-06-26 22:26  wangyucheng  阅读(182)  评论(0编辑  收藏  举报