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

16 篇文章 0 订阅

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<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#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 sum<b.sum;}
};
priority_queue <data> heap;
void ST(){
    for(int j=1;j<=log2(n);j++)
     for(int i=0;i<=n-(1<<j)+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<<j)+1][j])?g[l][j]:g[r-(1<<j)+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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值