bzoj千题计划162:bzoj2006: [NOI2010]超级钢琴

http://www.lydsy.com/JudgeOnline/problem.php?id=2006

 

输出最大的k个 sum[r]-sum[l-1] (L<=r-l+1<=R) 之和

当右端点固定不变时,左端点的前缀和越小越好

固定右端点r后,左端点的被限制在了区间[r-R,r-L]内

RMQ查出在这段左端点区间内,左端点前缀和的最小值

把所有的这些放到一个大根堆里

取出一个元素后

若原区间[a,b] 左端点选的位置是p

那么原区间分裂为两个区间[a,p-1] 和 [p+1,b]

即 若原来区间的右端点是End,左端点可选区间为[a,b]

那么当[a,b]内选位置p当左端点,且作为前k大用过后,

右端点为End的区间可选左端点变成了 [a,p-1],[p+1,b],放入堆中即可

 

#include<queue>
#include<cmath>
#include<cstdio>
#include<iostream>
#include<algorithm>

using namespace std;

#define N 500001

struct node
{
    int l,r;
    int End;
    int pos,val;
    
    bool operator < (node p) const
    {
        return val<p.val;
    } 
}nxt;

priority_queue<node>q;

int sum[N];
int mi[N][20];

void read(int &x)
{
    x=0; int f=1; char c=getchar();
    while(!isdigit(c)) { if(c=='-') f=-1; c=getchar(); }
    while(isdigit(c)) { x=x*10+c-'0'; c=getchar(); }
    x*=f;
}

int find(int l,int r)
{
    int k=log(r-l+1)/log(2);
    return sum[mi[l][k]]<sum[mi[r-(1<<k)+1][k]] ? mi[l][k] :  mi[r-(1<<k)+1][k];
}

int main()
{
    freopen("piano.in","r",stdin);
    freopen("piano.out","w",stdout);
    int n,k,L,R;
    read(n);
    read(k);
    read(L);
    read(R);
    for(int i=1;i<=n;++i)
    {
        read(sum[i]);
        sum[i]+=sum[i-1];
        mi[i][0]=i;
    }
    for(int j=1,k=1;k<<1<=n;++j,k<<=1)
        for(int i=0;i+(k<<1)-1<=n;++i)
            if(sum[mi[i][j-1]]<sum[mi[i+k][j-1]]) mi[i][j]=mi[i][j-1];
            else mi[i][j]=mi[i+k][j-1];
    node tmp;
    for(int i=1;i<=n;++i)
    {
        tmp.End=i;
        tmp.l=max(i-R,0);
        tmp.r=i-L;
        if(tmp.l>tmp.r) continue;
        tmp.pos=find(tmp.l,tmp.r);
        tmp.val=sum[i]-sum[tmp.pos];
        q.push(tmp);
    }
    long long ans=0;
    while(k--)
    {
        tmp=q.top();
        ans+=tmp.val;
        q.pop();
        nxt.End=tmp.End;
        if(tmp.pos!=tmp.l)
        {
            nxt.pos=find(tmp.l,tmp.pos-1);
            nxt.l=tmp.l;
            nxt.r=tmp.pos-1;
            nxt.val=sum[nxt.End]-sum[nxt.pos];
            q.push(nxt);
        }
        if(tmp.pos!=tmp.r)
        {
            nxt.pos=find(tmp.pos+1,tmp.r);
            nxt.l=tmp.pos+1;
            nxt.r=tmp.r;
            nxt.val=sum[nxt.End]-sum[nxt.pos];
            q.push(nxt);
        }
    }
    cout<<ans;
}

 

2006: [NOI2010]超级钢琴

Time Limit: 20 Sec  Memory Limit: 552 MB
Submit: 3473  Solved: 1714
[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,表示按编号从小到大每个音符的美妙度。
N<=500,000
k<=500,000
-1000<=Ai<=1000,1<=L<=R<=N且保证一定存在满足条件的乐曲

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

posted @ 2017-12-29 15:34  TRTTG  阅读(222)  评论(0编辑  收藏  举报