BZOJ_2006_[NOI2010]超级钢琴_贪心+堆+ST表

BZOJ_2006_[NOI2010]超级钢琴_贪心+堆+ST表

Description

小Z是一个小有名气的钢琴家,最近C博士送给了小Z一架超级钢琴,小Z希望能够用这架钢琴创作出世界上最美妙的
音乐。 这架超级钢琴可以弹奏出n个音符,编号为1至n。第i个音符的美妙度为Ai,其中Ai可正可负。 一个“超级
和弦”由若干个编号连续的音符组成,包含的音符个数不少于L且不多于R。我们定义超级和弦的美妙度为其包含的
所有音符的美妙度之和。两个超级和弦被认为是相同的,当且仅当这两个超级和弦所包含的音符集合是相同的。 
小Z决定创作一首由k个超级和弦组成的乐曲,为了使得乐曲更加动听,小Z要求该乐曲由k个不同的超级和弦组成。
我们定义一首乐曲的美妙度为其所包含的所有超级和弦的美妙度之和。小Z想知道他能够创作出来的乐曲美妙度最
大值是多少。

Input

第一行包含四个正整数n, k, L, R。其中n为音符的个数,k为乐曲所包含的超级和弦个数,L和R分别是超级和弦所
包含音符个数的下限和上限。 接下来n行,每行包含一个整数Ai,表示按编号从小到大每个音符的美妙度。
N<=500,000
k<=500,000
-1000<=Ai<=1000,1<=L<=R<=N且保证一定存在满足条件的乐曲

Output

只有一个整数,表示乐曲美妙度的最大值。

Sample Input

4 3 2 3
3
2
-6
8

Sample Output

11


把所有的子串当成后缀的前缀来做。

那么对于从i这个位置开始的子串,有一个可以选取末位置j的范围[i+L-1,i+R-1]。

假设最大的合法子串在j这个位置,那么次大值一定出现在[i+L-1,j-1]和[j+1,i+R-1]中。

于是有了这样的想法:用堆来维护四元组<i,p,l,r>表示从i到p的子段,选择的范围从l到r。

然后每次查询区间最大值用ST表做即可。

代码:

#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <ext/pb_ds/priority_queue.hpp>
using namespace std;
using namespace __gnu_pbds;
typedef long long ll;
#define N 500050
int n,a[N],s[N],K,L,R,f[N][21],Lg[N],g[N][21];
ll ans;
struct A {
    int x,p,l,r;
    A() {}
    A(int x_,int p_,int l_,int r_) :
        x(x_),p(p_),l(l_),r(r_) {}
};
bool operator < (const A &x,const A &y) {
    return s[x.p]-s[x.x-1]<s[y.p]-s[y.x-1];
}
__gnu_pbds::priority_queue<A>q;
int get_max(int l,int r) {
    int len=Lg[r-l+1];
    return f[l][len]>f[r-(1<<len)+1][len]?g[l][len]:g[r-(1<<len)+1][len];
}
void solve() {
    int i;
    for(i=1;i<=n-L+1;i++) {
        int t=get_max(i+L-1,min(n,i+R-1));
        q.push(A(i,t,i+L-1,min(n,i+R-1)));
    }
    for(i=1;i<=K;i++) {
        A t=q.top(); q.pop();
        //printf("%d %d
",t.x,t.p);
        ans+=s[t.p]-s[t.x-1];
        if(t.p>t.l) q.push(A(t.x,get_max(t.l,t.p-1),t.l,t.p-1));
        if(t.p<t.r) q.push(A(t.x,get_max(t.p+1,t.r),t.p+1,t.r));
    }
}
int main() {
    scanf("%d%d%d%d",&n,&K,&L,&R);
    int i,j;
    Lg[0]=-1;
    for(i=1;i<=n;i++) {
        scanf("%d",&a[i]);
        s[i]=s[i-1]+a[i];
        f[i][0]=s[i];
        g[i][0]=i;
        Lg[i]=Lg[i>>1]+1;
    }
    Lg[0]=0;
    for(j=1;(1<<j)<=n;j++) {
        for(i=1;i+(1<<j)-1<=n;i++) {
            if(f[i][j-1]>f[i+(1<<j-1)][j-1]) {
                f[i][j]=f[i][j-1];
                g[i][j]=g[i][j-1];
            }else {
                f[i][j]=f[i+(1<<j-1)][j-1];
                g[i][j]=g[i+(1<<j-1)][j-1];
            }
        }
    }
    solve();
    printf("%lld
",ans);
}
原文地址:https://www.cnblogs.com/suika/p/9022822.html