洛谷 P2048 [NOI2010]超级钢琴 解题报告

2023-07-30,,

P2048 [NOI2010]超级钢琴

题目描述

小Z是一个小有名气的钢琴家,最近C博士送给了小Z一架超级钢琴,小Z希望能够用这架钢琴创作出世界上最美妙的音乐。

这架超级钢琴可以弹奏出n个音符,编号为1至n。第i个音符的美妙度为Ai,其中Ai可正可负。

一个“超级和弦”由若干个编号连续的音符组成,包含的音符个数不少于L且不多于R。我们定义超级和弦的美妙度为其包含的所有音符的美妙度之和。两个超级和弦被认为是相同的,当且仅当这两个超级和弦所包含的音符集合是相同的。

小Z决定创作一首由k个超级和弦组成的乐曲,为了使得乐曲更加动听,小Z要求该乐曲由k个不同的超级和弦组成。我们定义一首乐曲的美妙度为其所包含的所有超级和弦的美妙度之和。小Z想知道他能够创作出来的乐曲美妙度最大值是多少。

输入输出格式

输入格式:

输入第一行包含四个正整数n, k, L, R。其中n为音符的个数,k为乐曲所包含的超级和弦个数,L和R分别是超级和弦所包含音符个数的下限和上限。

接下来n行,每行包含一个整数Ai,表示按编号从小到大每个音符的美妙度。

输出格式:

输出只有一个整数,表示乐曲美妙度的最大值。

说明

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


题意:求前\(k\)大长度在\(L\)~\(R\)之间的子序列之和

用堆维护\(ST\)表的做法没想到。。

想了个暴力\(nlog^2n\)的做法

枚举子序列左端点\(l\),则可能的右端点为一个长为\(R+1-L\)的序列,我们发现对于可能的右端点\(r\),产生的答案为\(f[r]-f[l-1]\),则每一个左端点固定后减去的值是一定的,只是产生的右端点划过去了。当然每个左端点可能产生多个子序列,我们不能只知道它的最大值。这时候,我们就可以想到拿主席树维护前缀和数组的静态区间第\(k\)值了,然后用一个堆维护每一个左端点所对应的区间取到第几大了。

然而我主席树一直写的不熟调了好久。。


Code:

#include <cstdio>
#include <queue>
#include <algorithm>
#include <iostream>
#define ll long long
using namespace std;
const ll N=2000010;
ll n,k,l0,r0;
ll d[N],score[N],root[N],dat[N<<4],L[N<<4],R[N<<4],num[N],cnt;
pair <ll ,ll > f[N];
ll build(ll l,ll r)
{
ll now=++cnt;
if(l==r) return now;
ll mid=l+r>>1;
L[now]=build(l,mid);
R[now]=build(mid+1,r);
return now;
}
ll rebuild(ll last,ll l,ll r,ll pos)
{
ll now=++cnt;
if(l==r)
{
dat[now]=1;
return now;
}
ll mid=l+r>>1;
if(pos<=mid)
{
L[now]=rebuild(L[last],l,mid,pos);
R[now]=R[last];
}
else
{
L[now]=L[last];
R[now]=rebuild(R[last],mid+1,r,pos);
}
dat[now]=dat[L[now]]+dat[R[now]];
return now;
}
ll query(ll last,ll now,ll l,ll r,ll num)
{
if(l==r) return l;
ll mid=l+r>>1;
if(dat[L[now]]-dat[L[last]]>=num)
return query(L[last],L[now],l,mid,num);
else
return query(R[last],R[now],mid+1,r,num-(dat[L[now]]-dat[L[last]]));
}
void init()
{
scanf("%lld%lld%lld%lld",&n,&k,&l0,&r0);
for(ll i=1;i<=n;i++)
{
scanf("%lld",&score[i]);
score[i]+=score[i-1];
f[i].second=i;
f[i].first=score[i];
}
sort(f+1,f+1+n);
for(ll i=1;i<=n;i++)
{
num[f[i].second]=n-i+1;
d[n-i+1]=f[i].first;
}
root[0]=build(1,n);
for(ll i=1;i<=n;i++)
root[i]=rebuild(root[i-1],1,n,num[i]);
}
priority_queue <pair <ll,ll > > q;
pair <ll,ll > p;
ll top[N];
void work()
{
for(ll i=l0;i<=n;i++)
{
ll r=min(i+r0-l0,n);
p.first=d[query(root[i-1],root[r],1,n,++top[i])]-score[i-l0];
p.second=i;
q.push(p);
}
ll ans=0;
while(k--)
{
ll now=q.top().second;
ans+=q.top().first;
q.pop();
if(top[now]>=min(r0-l0,n-now)+1) continue;
p.first=d[query(root[now-1],root[min(now+r0-l0,n)],1,n,++top[now])]-score[now-l0];
p.second=now;
q.push(p);
}
printf("%lld\n",ans);
}
int main()
{
init();
work();
return 0;
}

2018.7.5

洛谷 P2048 [NOI2010]超级钢琴 解题报告的相关教程结束。

《洛谷 P2048 [NOI2010]超级钢琴 解题报告.doc》

下载本文的Word格式文档,以方便收藏与打印。