首页 > 代码库 > [NOI2010]超级钢琴 划分树+堆

[NOI2010]超级钢琴 划分树+堆

#include<iostream>#include<cstdio>#include<algorithm>#include<cstring>#include<queue>using namespace std;#define N 510000struct 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;}