NOI2010——超级钢琴(划分树)

本文解析了NOI2010超级钢琴问题,通过寻找满足条件的和弦组合以最大化乐曲美妙度。介绍了使用优先队列和划分树的数据结构来解决此问题的方法。
2006: [NOI2010]超级钢琴

Time Limit: 20 Sec  Memory Limit: 552 MB
Submit: 954  Solved: 416
[Submit][Status][Discuss]
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
Source

Day1

解析: 

          本题实际上就是求满足大于L小于R的所有区间的最大值的前K大的和。。。

          枚举所有区间结束位置,从i-R到i-L中找前缀和最小的起始位置,使得该区间总和最大。。。

          将所的区间总和入优先队列。。。每次出队一个值。。。

          就要找到该值所对应的的结束位置,找以此位置结束的总和次大值。。。并入队(后面以此类推)

          找次大,以及后面找第k大就可以用划分树。。。

代码:

/**************************************************************
    Problem: 2006
    User: jianing
    Language: C++
    Result: Accepted
    Time:2668 ms
    Memory:135536 kb
****************************************************************/
 
#include<cstring>
#include<cstdio>
#include<queue>
#include<algorithm>
using namespace std;
#define MAXN 510001
 
struct node
{
     int num,k,val;
};
priority_queue<node> q;
long long ans=0;
int n,k,L,R,a[MAXN];
int sum[MAXN],val[30][MAXN],order[MAXN],num[30][MAXN];
bool operator < (const node &A,const node &B)
{
    return A.val<B.val;
}
 
void build(int h,int l,int r)
{
    if(l==r)return ;
    int mid=(l+r)>>1;
    int same=mid-l+1;
    for(int i=l;i<=r;i++)
        if(val[h][i]<order[mid])same--;
    int cl=l;int cr=mid+1;
    for(int i=l;i<=r;i++)
    {
        int flag=0;
        if(val[h][i]<order[mid]||(val[h][i]==order[mid]&&same))
        {
            flag=1;
            val[h+1][cl++]=val[h][i];
            if(val[h][i]==order[mid])same--;
        }
        else
            val[h+1][cr++]=val[h][i];
        num[h][i]=num[h][i-1]+flag;
    }
    build(h+1,l,mid);
    build(h+1,mid+1,r);
}
 
int query(int h,int ll,int rr,int l,int r,int k)
{
    if(ll==rr)return val[h][ll];
    int mid=(ll+rr)>>1;
    int lx=num[h][l-1]-num[h][ll-1];
    int ly=num[h][r]-num[h][l-1];
    int rx=l-ll-lx;
    int ry=r-l+1-ly;
    if(k<=ly)return query(h+1,ll,mid,ll+lx,ll+lx+ly-1,k);
    else return query(h+1,mid+1,rr,mid+1+rx,mid+1+rx+ry-1,k-ly);
}
 
void read()
{
    //freopen("piano.in","r",stdin);
    //freopen("piano.out","w",stdout);
    memset(sum,0,sizeof(sum));
    scanf("%d%d%d%d",&n,&k,&L,&R);
    int i;
     for(i=1;i<=n;i++)
     {
         scanf("%d",&a[i]);
         sum[i+1]=sum[i]+a[i];
         order[i+1]=val[0][i+1]=sum[i+1];
     }
    n++;
    sort(order+1,order+1+n);
    build(0,1,n);
}
 
void work()
{
    for(int i=L+1;i<=n;i++)//枚举区间右界
    {
        node temp;
        temp.val=sum[i]-query(0,1,n,max(i-R,1),max(i-L,1),1);
        temp.num=i;
        temp.k=1;
        q.push(temp);
    }
    for(int i=1;i<=k;i++)
    {
        node tmp=q.top();q.pop();
        ans+=tmp.val;
        int t=tmp.num;
        if(max(t-L,1)-max(t-R,1)+1>tmp.k)
        {
            tmp.k++;
            tmp.val=sum[t]-query(0,1,n,max(t-R,1),max(t-L,1),tmp.k);
            q.push(tmp);
        }
    }
    printf("%lld\n",ans);
}
 
int main()
{
    read();
    work();
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值