bzoj2006 [NOI2010]超级钢琴

90 篇文章 0 订阅
14 篇文章 0 订阅

Description


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

N<=500,000
k<=500,000
-1000<=Ai<=1000,1<=L<=R<=N且保证一定存在满足条件的乐曲

Solution


看到求区间和就前缀和,记录点i向左最多和最少能拓展到哪个位置,查询区间最小值就能知道以i为结束点的最优解了
把这样一堆东西扔到优先队列里面每次找最大值,找完就把区间[l,r]分裂成[l,pos-1]和[pos+1,r],其中pos是区间[l,r]的最小值
理论上说线段树改st表能跑得更快的说

Code


#include <stdio.h>
#include <string.h>
#include <queue>
#define rep(i,st,ed) for (int i=st;i<=ed;++i)

typedef long long LL;
const int INF=2000000005;
const int N=500005;

int mn[N*4],p[N*4],a[N];
LL sum[N];

struct data {
    int i,l,r,pos;
    bool operator <(data b) const {
        return sum[i]-sum[pos]<sum[b.i]-sum[b.pos];
    }
};

std:: priority_queue <data> heap;

void modify(int now,int tl,int tr,int x,int v) {
    if (tl==tr) {
        mn[now]=v;
        p[now]=tl;
        return ;
    }
    int mid=(tl+tr)>>1;
    if (x<=mid) modify(now<<1,tl,mid,x,v);
    else modify(now<<1|1,mid+1,tr,x,v);
    if (mn[now<<1]<mn[now<<1|1]) p[now]=p[now<<1];
    else p[now]=p[now<<1|1];
    mn[now]=std:: min(mn[now<<1],mn[now<<1|1]);
}

int query(int now,int tl,int tr,int l,int r) {
    if (tl==l&&tr==r) return p[now];
    int mid=(tl+tr)>>1;
    if (r<=mid) return query(now<<1,tl,mid,l,r);
    else if (l>mid) return query(now<<1|1,mid+1,tr,l,r);
    else {
        int qx=query(now<<1,tl,mid,l,mid);
        int qy=query(now<<1|1,mid+1,tr,mid+1,r);
        if (sum[qx]<sum[qy]) return qx;
        return qy;
    }
}

void buildTree(int now,int tl,int tr) {
    mn[now]=INF;
    if (tl==tr) return ;
    int mid=(tl+tr)>>1;
    buildTree(now<<1,tl,mid);
    buildTree(now<<1|1,mid+1,tr);
}

int main(void) {
    freopen("data.in","r",stdin);
    int n,m,L,R; scanf("%d%d%d%d",&n,&m,&L,&R);
    buildTree(1,0,n);
    modify(1,0,n,0,0);
    rep(i,1,n) {
        scanf("%d",&a[i]);
        sum[i]=sum[i-1]+a[i];
        modify(1,0,n,i,sum[i]);
    }
    rep(i,L,n) {
        data d=(data) {i,std:: max(0,i-R),std:: max(0,i-L)};
        d.pos=query(1,0,n,d.l,d.r);
        heap.push(d);
    }
    LL ans=0;
    while (m--) {
        data top=heap.top(); heap.pop();
        ans+=sum[top.i]-sum[top.pos];
        data d1=top,d2=top;
        d1.r=top.pos-1; d2.l=top.pos+1;
        if (d1.l<=d1.r) {
            d1.pos=query(1,0,n,d1.l,d1.r);
            heap.push(d1);
        }
        if (d2.l<=d2.r) {
            d2.pos=query(1,0,n,d2.l,d2.r);
            heap.push(d2);
        }
    }
    printf("%lld\n", ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值