【NOI2010】【BZOJ2006】超级钢琴

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,表示按编号从小到大每个音符的美妙度。
Output
只有一个整数,表示乐曲美妙度的最大值。
Sample Input
4 3 2 3

3

2

-6

8

Sample Output
11

【样例说明】
共有5种不同的超级和弦:

音符1 ~ 2,美妙度为3 + 2 = 5
音符2 ~ 3,美妙度为2 + (-6) = -4
音符3 ~ 4,美妙度为(-6) + 8 = 2
音符1 ~ 3,美妙度为3 + 2 + (-6) = -1
音符2 ~ 4,美妙度为2 + (-6) + 8 = 4
最优方案为:乐曲由和弦1,和弦3,和弦5组成,美妙度为5 + 2 + 4 = 11。

HINT数据范围及提示 Data Size & Hint
0< N<=500000,0< k<=50000

所有数据满足:-1000 ≤ Ai ≤ 1000,1 ≤ L ≤ R ≤ n且保证一定存在满足要求的乐曲。

Source

Day1

考虑用三元组[l,r1,r2]表示在所有区间[l,r1],[l,r1+1],…,[l,r2]这些区间里区间和最大值的最大值,并记录取到最大值的位置.线段树维护一个前缀和加上那个位置就行.
然后将每个[i,i+l-1,i+r-1]加入优先队列,k次取优先队列顶元素累积答案,如果取到最大值的位置pos,再将[i, pos+1, i+r-1]和[i,i+l-1, pos-1]重新加入堆.(他们也有可能成为新的最大元素).

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<queue>
#include<algorithm>
#define MAXN 500100
#define GET (ch>='0'&&ch<='9')
#define lchild rt<<1,l,mid
#define rchild rt<<1|1,mid+1,r
#define ln rt<<1
#define rn rt<<1|1
#define LL long long
using namespace std;
LL n,k,_l,_r;
LL ans,a[MAXN];
struct seg
{
    int l,r;
    LL maxn,pos;
}tree[MAXN<<2];
struct node
{
    int i,l,r,maxn,pos;
    inline bool operator <(const node& a)const
    {
        return maxn<a.maxn;
    }
};
priority_queue<node>    heap;
inline void push_up(int rt)
{
    if (tree[ln].maxn<=tree[rn].maxn)   tree[rt].maxn=tree[rn].maxn,tree[rt].pos=tree[rn].pos;
    else    tree[rt].maxn=tree[ln].maxn,tree[rt].pos=tree[ln].pos;
}
void build(int rt=1,int l=1,int r=n)
{
    tree[rt].l=l;tree[rt].r=r;int mid=l+r>>1;
    if (l==r)   {tree[rt].maxn=a[l];tree[rt].pos=l;return;}
    build(lchild);build(rchild);push_up(rt);
}
seg query(int rt,int l,int r)
{
    int L=tree[rt].l,R=tree[rt].r,mid=L+R>>1;seg ret;ret.maxn=ret.pos=0;
    if (l<=L&&R<=r) return tree[rt];
    if (r<=mid) return query(ln,l,r);
    if (l>mid)  return query(rn,l,r);
    seg a=query(ln,l,mid),b=query(rn,mid+1,r);ret=a.maxn>b.maxn?a:b;
    return ret;
}
void in(long long &x)
{
    x=0;char ch=getchar();int flag=1;
    while (!GET)    flag=ch=='-'?-1:1,ch=getchar();
    while (GET) x=x*10+ch-'0',ch=getchar();x*=flag;
}
int main()
{
    in(n);in(k);in(_l);in(_r);node tmp,temp;seg t;
    for (int i=1;i<=n;i++)  in(a[i]),a[i]=a[i-1]+a[i];build();
    for (int i=1;i<=n;i++)//init
    {
        temp.i=i;temp.l=i+_l-1;temp.r=min(i+_r-1,n);if (temp.l>n)   continue;
        t=query(1,temp.l,temp.r);temp.maxn=t.maxn-a[i-1];temp.pos=t.pos;heap.push(temp);
    }
    for (int i=1;i<=k;i++)//get answer
    {
        temp=heap.top();heap.pop();ans+=temp.maxn;
        if (temp.l<temp.pos)
        {
            tmp.i=temp.i;tmp.l=temp.l;tmp.r=temp.pos-1;
            t=query(1,tmp.l,tmp.r);tmp.maxn=t.maxn-a[tmp.i-1];tmp.pos=t.pos;heap.push(tmp);
        }
        if (temp.r>temp.pos)
        {
            tmp.i=temp.i;tmp.l=temp.pos+1;tmp.r=temp.r;
            t=query(1,tmp.l,tmp.r);tmp.maxn=t.maxn-a[tmp.i-1];tmp.pos=t.pos;heap.push(tmp);
        }
    }
    cout<<ans<<endl;    
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值