【bzoj2006】【NOI2010】【超级钢琴】

原创 2015年07月10日 14:41:07

2006: [NOI2010]超级钢琴

Time Limit: 20 Sec Memory Limit: 552 MB
Submit: 1778 Solved: 871
[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。

这里写图片描述

我们处理出这样一个东西(i,L,R)表示当这个和弦的左端点为i时在上限和下限中的最优值。(也就是右端点在[i+l-1,i+r-1]中)
我们将第一步处理出来的所有最优值扔到一个堆里面,然后每次从堆中选取最大的出来,将ans加上这个数。假设我们在这段区间中选取最优值得位置为where,那么我们就这段区间[L,R]裂解成两段区间[L,where-1]和[where+1,R],再讲这两段区间放到堆里面。
这样其实就是每次选取了一个最优值,然后把那些小于最优值的数放进去。
选取了K次之后就是答案。

我们对于第一步的处理,其实有两种方法:
首先我们都需要处理出前缀和这个东西来,然后就是查询区间最值,查完后再加上i到L的值就好了。对于查区间最值:
①:我们可以用线段树。但是我们的查询次数是(n+k),所以会比较慢。
②:我们可以用ST(一种跟倍增差不多的东西),用ST就可以做到O(1)的查询,会快很多

然后堆就直接STL把,手写比较麻烦
(由于我比较弱,想了一下午才想出怎么做。。。)

线段树+priority_queue

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
#define inf 210000000LL
const int N=500100;
int n,K,a[N];
long long ans=0;
struct S{
    int where,maxn;
    void init(){
        maxn=-inf;
        where=0;
    }
}tr[N*4];
struct heap{int maxn,where,left,llimit,rlimit;};
priority_queue<heap> q;
bool operator < (heap x,heap y){return x.maxn<y.maxn;}
int in()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
#define mid (l+r)/2
#define L k<<1,l,mid
#define R k<<1|1,mid+1,r
void build(int k,int l,int r)
{
    if(l==r){
        tr[k].maxn=a[l];
        tr[k].where=l;
        return ;
    }
    build(L); build(R);
    tr[k].maxn=max(tr[k<<1].maxn,tr[k<<1|1].maxn);
    if(tr[k<<1].maxn>tr[k<<1|1].maxn) tr[k].where=tr[k<<1].where;
    if(tr[k<<1].maxn<=tr[k<<1|1].maxn) tr[k].where=tr[k<<1|1].where;
}
S ask(int k,int l,int r,int x,int y)
{
    S ans1,ans2;
    ans1.init();ans2.init();
    if(x<=l&&y>=r) return tr[k];
    if(x<=mid) ans1=ask(L,x,y);
    if(y>mid) ans2=ask(R,x,y);
    if(ans1.maxn<ans2.maxn){
        ans1.maxn=ans2.maxn;
        ans1.where=ans2.where;
    }
    return ans1;
}
int main()
{
    int i,j,l,r,ll,rr;
    S t;
    heap ans1;
    n=in();K=in();l=in();r=in();
    for(i=1;i<=n;++i) a[i]=in(),a[i]+=a[i-1];
    build(1,1,n);   
    for(i=1;i<=n-l+1;++i){
        ll=i+l-1,rr=min(i+r-1,n);
        t=ask(1,1,n,ll,rr);
        q.push((heap){t.maxn-a[i-1],t.where,i,ll,rr});
    }
    while(K--){
        ans1=q.top();
        q.pop();
        ans+=(long long)ans1.maxn;
        if(ans1.where-1>=ans1.llimit){
            t=ask(1,1,n,ans1.llimit,ans1.where-1);
            q.push((heap){t.maxn-a[ans1.left-1],t.where,ans1.left,ans1.llimit,ans1.where-1});
        }
        if(ans1.where+1<=ans1.rlimit){
            t=ask(1,1,n,ans1.where+1,ans1.rlimit);
            q.push((heap){t.maxn-a[ans1.left-1],t.where,ans1.left,ans1.where+1,ans1.rlimit});
        }
    }
    printf("%lld\n",ans);
}

ST+priority_queue

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<cmath> 
using namespace std;
#define inf 210000000LL
const int N=500100;
int n,K,a[N];
long long ans=0;
struct S{int maxn,where;}f[20][N];
struct heap{int maxn,where,left,llimit,rlimit;};
priority_queue<heap> q;
bool operator < (heap x,heap y){return x.maxn<y.maxn;}
int in()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
void prepare()
{
    int i,j;
    for(i=1;i<=20;++i)
      for(j=1;j+(1<<i)-1<=n;++j){
        f[i][j].maxn=max(f[i-1][j].maxn,f[i-1][j+(1<<i>>1)].maxn);
        if(f[i-1][j].maxn>f[i-1][j+(1<<i>>1)].maxn) f[i][j].where=f[i-1][j].where;
        else f[i][j].where=f[i-1][j+(1<<i>>1)].where;
      }
}
S ask(int x,int y)
{
  S ans1;
  int t=(int)(log(y-x+1.0)/log(2.0));
  ans1.maxn=max(f[t][x].maxn,f[t][y-(1<<t)+1].maxn);
  if (f[t][x].maxn>f[t][y-(1<<t)+1].maxn) ans1.where=f[t][x].where;
  else ans1.where=f[t][y-(1<<t)+1].where;
  return ans1;
}
int main()
{
    int i,j,l,r,ll,rr;
    S t;
    heap ans1;
    n=in();K=in();l=in();r=in();
    for(i=1;i<=n;++i){
        a[i]=in();
        a[i]+=a[i-1];
        f[0][i].maxn=a[i];
        f[0][i].where=i;
    } 
    prepare();
    for(i=1;i<=n-l+1;++i){
        ll=i+l-1,rr=min(i+r-1,n);
        t=ask(ll,rr);
        q.push((heap){t.maxn-a[i-1],t.where,i,ll,rr});
    }
    while(K--){
        ans1=q.top();
        q.pop();
        ans+=(long long)ans1.maxn;
        if(ans1.where-1>=ans1.llimit){
            t=ask(ans1.llimit,ans1.where-1);
            q.push((heap){t.maxn-a[ans1.left-1],t.where,ans1.left,ans1.llimit,ans1.where-1});
        }
        if(ans1.where+1<=ans1.rlimit){
            t=ask(ans1.where+1,ans1.rlimit);
            q.push((heap){t.maxn-a[ans1.left-1],t.where,ans1.left,ans1.where+1,ans1.rlimit});
        }
    }
    printf("%lld\n",ans);
}
版权声明:本文为博主原创文章,未经博主允许不得转载。

相关文章推荐

【NOI2010】超级钢琴

题目大意给出一个长度为n的序列,求所有长度在L到R之间的连续子序列的权值和的前k大的和解法首先将以每个点为开头的长度在L到R之间的序列的权值和最大的区间扔进堆里,每次拿出堆顶,然后再放入以当前点为开头...

NOI2010.Day1.T2.超级钢琴

题目大意: 在n个数字中找出k个不相同的长度在l-r之间的连续子序列,使得权值和最大(n 昨天膜拜了一下10年的年鉴 这道题合法的子序列是非常多的,如果朴素显然是无法做出这道题 有一个...
  • OrpineX
  • OrpineX
  • 2011年11月25日 17:34
  • 2380

[NOI2010]超级钢琴 解题报告

超级钢琴 【问题描述】 小Z是一个小有名气的钢琴家,最近C博士送给了小Z一架超级钢琴,小Z希望能够用这架钢琴创作出世界上最美妙的音乐。 这架超级钢琴可以弹奏出n个音符,编号为1至n。第i个音...

BZOJ2006 [NOI2010]超级钢琴

题目大意:给定一个序列,求出k个这个序列的位置不完全相同的子序列,使得每一个子序列的长度均在[l,r]内,并且使得这些子序列的权值和最大。 思路:每一个子序列的权值和可以转化为两个前缀和之差...

[BZOJ2006] [NOI2010]超级钢琴

[BZOJ2006] [NOI2010]超级钢琴题目大意给定一个序列,要求找到连续的序列满足长度在[L,R][L,R]范围内,询问前kk大的满足条件的序列的和题解...

bzoj2006 [ NOI2010 ] && bzoj3784 --点分治+线段树+堆

bzoj2006: 定义一个四元组{x,l,r,w},表示左端点在x,右端点在[l,r]的超级和弦的最大美妙度在将w作为右端点时取到,w可以用前缀和+线段树/ST表求出。 对于每个i,我们将{i,i+...
  • gjghfd
  • gjghfd
  • 2017年05月26日 16:42
  • 74

超级钢琴

超级钢琴时间限制: 2 Sec 内存限制: 512 MB 题目描述 小Z是一个小有名气的钢琴家,最近C博士送给了小Z一架超级钢琴,小Z希望能够用这架钢琴创作出世界上最美妙的音乐。 这架超级钢琴...

BW中使用abap

前言 本文的目的是讲述如何在BW中使用ABAP编程。在每一个项目中,正确使用ABAP编程会对正确的业务建模起到积极作用。在设计和蓝图阶段,一个核心的功能流程是类比于如何在BW中完成类似于R3中的业务...

2006: [NOI2010]超级钢琴

RMQ+Heap+区间分裂。 对于任意区间右端点i,其左端点取值在l,r之间,若左端点为m,则v为max(sum[i]-sum[m-1]),显然这里i是不变的,所以可以用rmq查询m的位置,然后计算...
  • nlj1999
  • nlj1999
  • 2015年12月23日 11:40
  • 233

【雅礼联考GDOI2017模拟9.2】Ztxz16学图论

Description众所周知,Zjr506是算法之神,因此Ztxz16经常向他请教算法。这一天,Zjr506在教导了Ztxz16关于图论方面的一些算法后,给他出了一道图论题作为家庭作业: 给定N个...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:【bzoj2006】【NOI2010】【超级钢琴】
举报原因:
原因补充:

(最多只允许输入30个字)