bzoj 2006: [NOI2010]超级钢琴 可持久化线段树+优先队列

24 篇文章 0 订阅
18 篇文章 0 订阅

题意

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

分析

考虑维护前缀和。
对于序列每一个i,我们可以扔一个三元组(id,val,rank)进优先队列里面。id等于i,val表示以i结尾且对答案贡献最大的序列和,rank表示这个贡献对于结尾i而言是第几大的。
那么我们可以每次取堆顶元素,然后把(id,val’,rank+1)扔进优先队列里面。val’表示以id结尾且对答案的贡献为第rank+1大的序列和。
如何快速求出val’呢?我们可以用一棵可持久化线段树来维护前缀和,即可资瓷求区间K大。
复杂度O(klogn)

代码

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;

typedef long long LL;

const int N=500005;

int n,m,L,R,val[N],a[N],a1,root[N],sz,num[N];
struct data
{
    int rank,val,id;

    bool operator < (const data a) const
    {
        return val<a.val;
    }
};
struct tree{int l,r,s;}t[N*20];
priority_queue<data> q;

void ins(int &d,int p,int l,int r,int x)
{
    d=++sz;
    t[d]=t[p];t[d].s++;
    if (l==r) return;
    int mid=(l+r)/2;
    if (x<=mid) ins(t[d].l,t[p].l,l,mid,x);
    else ins(t[d].r,t[p].r,mid+1,r,x);
}

int kth(int d,int p,int l,int r,int k)
{
    if (l==r) return l;
    int mid=(l+r)/2;
    if (t[t[p].l].s-t[t[d].l].s>=k) return kth(t[d].l,t[p].l,l,mid,k);
    else return kth(t[d].r,t[p].r,mid+1,r,k-(t[t[p].l].s-t[t[d].l].s));
}

int main()
{
    scanf("%d%d%d%d",&n,&m,&L,&R);
    for (int i=2;i<=n+1;i++) scanf("%d",&val[i]),val[i]+=val[i-1],a[++a1]=val[i];
    a[++a1]=0;n++;
    sort(a+1,a+a1+1);
    a1=unique(a+1,a+a1+1)-a-1;
    for (int i=1;i<=n;i++) num[i]=lower_bound(a+1,a+a1+1,val[i])-a;
    for (int i=1;i<=n;i++) ins(root[i],root[i-1],1,a1,num[i]);
    for (int i=2;i<=n;i++)
    {
        if (i-L<1) continue;
        int w=a[kth(root[max(1,i-R)-1],root[i-L],1,a1,1)];
        data u;u.id=i;u.val=val[i]-w;u.rank=1;
        q.push(u);
    }
    LL ans=0;
    while (m--)
    {
        data u=q.top();q.pop();
        ans+=u.val;
        int i=u.id;
        if (i-L-max(1,i-R)+1==u.rank) continue;
        u.rank++;
        int w=a[kth(root[max(1,i-R)-1],root[i-L],1,a1,u.rank)];
        u.val=val[i]-w;
        q.push(u);
    }
    printf("%lld",ans);
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值