【NOI2010】超级钢琴

题目链接

设$s_i=\sum\limits_{j=1}^i a_j$,那么相同左端点$l$的区间之中,最大的区间和就是$\mathop{max}\limits_{i=l+L-1}^{i+R-1}\{s_i-s_{l-1}\}=\mathop{max}\limits_i\{s_i\}-s_{l-1}$。

那对前缀和做ST表就可以快速求解了。

设$f(i,l,r,t)$表示左端点是$i$,右端点在$[l,r]$范围内,使区间和最大的区间右端点是$t$,的最大区间和。

那我们可以把所有的$f(i,i+L-1,i+R-1,t)$丢进堆里,取$k$次,每次把右端点区间按$(l,t-1)$和$(t+1,r)$分开重新压进堆里。

 

代码(100分):

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
#include<queue>
#include<map>
#include<set>
#define IL inline
#define RG register
#define _1 first
#define _2 second
using namespace std;
typedef long long LL;
const int N=5e5;
const int L=18;

    int n,k,llim,rlim;
    LL a[N+3],s[N+3];
    
    int lg2[N+3];
    int f[N+3][L+3];
    
IL void init(){
    s[0]=0;
    for(int i=1;i<=n;i++)
        s[i]=s[i-1]+a[i];
    
    lg2[0]=lg2[1]=0;
    for(int i=2;i<=n;i++)
    if((1<<(lg2[i-1]+1))==i)
        lg2[i]=lg2[i-1]+1;
    else 
        lg2[i]=lg2[i-1];
    
    for(int i=1;i<=n;i++)
        f[i][0]=i;
    for(int l=1;l<=lg2[n];l++)
        for(int i=1;i<=n;i++){
            int j=i+(1<<(l-1));
            if(j>n||s[f[i][l-1]]>s[f[j][l-1]])
                f[i][l]=f[i][l-1];
            else 
                f[i][l]=f[j][l-1];
            
        }
    
}

IL int qry(int l,int r){
    int len=r-l+1,k=lg2[len],t=l+len-(1<<k);
    return (s[f[l][k]]>s[f[t][k]])?f[l][k]:f[t][k];
}

struct Dat{
    int i,l,r,k;    Dat(){}
    Dat(int i,int l,int r,int k)
        :i(i),l(l),r(r),k(k){}
};

IL bool operator<(Dat x,Dat y){    //warning: i differ
    return s[x.k]-s[x.i-1]<s[y.k]-s[y.i-1];
}

    priority_queue<Dat>hp;

int main(){
    scanf("%d%d%d%d",&n,&k,&llim,&rlim);
    for(int i=1;i<=n;i++)
        scanf("%lld",&a[i]);
    
    init();
    
    for(int i=1;i+llim-1<=n;i++){
        int l=i+llim-1,r=min(i+rlim-1,n);
        hp.push(Dat(i,l,r,qry(l,r)));
        
    }
    LL ans=0;
    while(k--){
        Dat x=hp.top();    hp.pop();
        ans+=s[x.k]-s[x.i-1];
        
        if(x.l<x.k)
            hp.push(Dat(x.i,x.l,x.k-1,qry(x.l,x.k-1)));
        if(x.k<x.r)
            hp.push(Dat(x.i,x.k+1,x.r,qry(x.k+1,x.r)));
        
    }
    
    printf("%lld",ans);

    return 0;

}
View Code

 

posted @ 2020-05-22 09:04  汉谡  阅读(140)  评论(0)    收藏  举报