BZOJ 2006: [NOI2010]超级钢琴【贪心】

Description

小Z是一个小有名气的钢琴家,最近C博士送给了小Z一架超级钢琴,小Z希望能够用这架钢琴创作出世界上最美妙的

音乐。 这架超级钢琴可以弹奏出n个音符,编号为1至n。第i个音符的美妙度为Ai,其中Ai可正可负。 一个“超级

和弦”由若干个编号连续的音符组成,包含的音符个数不少于L且不多于R。我们定义超级和弦的美妙度为其包含的

所有音符的美妙度之和。两个超级和弦被认为是相同的,当且仅当这两个超级和弦所包含的音符集合是相同的。

小Z决定创作一首由k个超级和弦组成的乐曲,为了使得乐曲更加动听,小Z要求该乐曲由k个不同的超级和弦组成。

我们定义一首乐曲的美妙度为其所包含的所有超级和弦的美妙度之和。小Z想知道他能够创作出来的乐曲美妙度最

大值是多少。

题解

这题是一类叫做“超级钢琴”的贪心。

如果我们能够处理处所有和弦的美妙度,那么直接取前 k 大的就行了,但是这是不可能的,所以,我们先处理出以每一个位置为左端点的最优解,在这些解中的最大值一定就是最大的和弦,然后,我们要把这个最优解的位置删去,获取剩下的位置的最优解,这样,如果令最优解的位置为 t ,以 i 为最短点的区间的右端点范围是 [L,R] t 这个点就将区间分割成了 [L,t1] [t+1,R] ,这样,只要维护一个堆,每次获取最优解和插入新节点都是 log 的复杂度,预处理用ST表就可以了。

代码

#include
#include
#include
#include
#include
#define maxn 500006
#define LL long long
using namespace std;
inline char nc(){
    static char buf[100000],*i=buf,*j=buf;
    return i==j&&(j=(i=buf)+fread(buf,1,100000,stdin),i==j)?EOF:*i++;
}
inline int _read(){
    char ch=nc();int sum=0,p=1;
    while(ch!='-'&&!(ch>='0'&&ch<='9'))ch=nc();
    if(ch=='-')p=-1,ch=nc();
    while(ch>='0'&&ch<='9')sum=sum*10+ch-48,ch=nc();
    return sum*p;
}
int n,T,L,R,sum[maxn],f[maxn][20],g[maxn][20];
LL ans;
struct data{
    int s,l,r,t,sum;
    bool operator <(const data&b)const{return sumsum;}
};
priority_queue  heap;
void ST(){
    for(int j=1;j<=log2(n);j++)
     for(int i=0;i<=n-(1<1;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];
}
inline int get(int l,int r){
    int j=log2(r-l+1);
    return (f[l][j]>f[r-(1<1][j])?g[l][j]:g[r-(1<1][j];
}
int main(){
    freopen("piano.in","r",stdin);
    freopen("piano.out","w",stdout);
    n=_read();T=_read();L=_read();R=_read();
    for(int i=1;i<=n;i++)sum[i]=sum[i-1]+_read(),f[i][0]=sum[i],g[i][0]=i;
    ST();
    for(int i=0;i<=n-L;i++){
        data p;
        p.s=i;p.l=i+L;p.r=min(n,i+R);
        p.t=get(p.l,p.r);p.sum=sum[p.t]-sum[i];
        heap.push(p);
    }
    while(T--){
        data p=heap.top(),p1,p2;heap.pop();
        ans+=p.sum;
        p1.s=p2.s=p.s;
        p1.l=p.l;p1.r=p.t-1;p2.l=p.t+1;p2.r=p.r;
        if(p1.l<=p1.r)p1.t=get(p1.l,p1.r),p1.sum=sum[p1.t]-sum[p1.s],heap.push(p1);
        if(p2.l<=p2.r)p2.t=get(p2.l,p2.r),p2.sum=sum[p2.t]-sum[p2.s],heap.push(p2);
    }
    printf("%lld",ans);
    return 0;
}

你可能感兴趣的:(BZOJ,贪心)