bzoj 2006: [NOI2010]超级钢琴
Description
小Z是一个小有名气的钢琴家,最近C博士送给了小Z一架超级钢琴,小Z希望能够用这架钢琴创作出世界上最美妙的
音乐。 这架超级钢琴可以弹奏出n个音符,编号为1至n。第i个音符的美妙度为Ai,其中Ai可正可负。 一个“超级
和弦”由若干个编号连续的音符组成,包含的音符个数不少于L且不多于R。我们定义超级和弦的美妙度为其包含的
所有音符的美妙度之和。两个超级和弦被认为是相同的,当且仅当这两个超级和弦所包含的音符集合是相同的。
小Z决定创作一首由k个超级和弦组成的乐曲,为了使得乐曲更加动听,小Z要求该乐曲由k个不同的超级和弦组成。
我们定义一首乐曲的美妙度为其所包含的所有超级和弦的美妙度之和。小Z想知道他能够创作出来的乐曲美妙度最
大值是多少。
solution
正解:贪心
套路题,这题我们显然是要取出前k大的子串,容易发现对于一个右端点R,记前缀和为 \(sum[i]\),那么一定是取出某个满足限制且最小的 \(sum[i]\),但是样例良心的提醒了我们:一个端点可以取次小和次次小等等,所以还需要一个套路:拆区间。我们定义一个子串 \((l,r,p,val,i)\),表示当前可以决策的区间是 \([l,r]\),上一次决策的最小值位置是 \(p\),区间和是 \(val\),右端点是 \(i\),的代价,我们把这个丢入堆中,然后没选出一个我们还需要拆 \([l,r]\) 为 \([l,p-1]\),\([p+1,r]\),因为一个端点的次小值还可以产生贡献,那么次小值一定是两个区间中的一个,再次把两个区间丢入堆中决策即可,对于最小值查询我们可以用到st表 \(O(1)\) 查询
#include <algorithm>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <queue>
#define RG register
#define il inline
#define iter iterator
#define Max(a,b) ((a)>(b)?(a):(b))
#define Min(a,b) ((a)<(b)?(a):(b))
using namespace std;
typedef long long ll;
const int N=500005;
struct node{
int l,r,p,val,id;
node(){}
node(int _l,int _r,int _p,int _val,int _id){
l=_l;r=_r;p=_p;val=_val;id=_id;
}
bool operator <(const node &pr)const{
return val<pr.val;
}
};
priority_queue<node>q;
int n,K,L,R,a[N],sum[N],f[N][20],Log[N],maxdep;
void priwork(){
for(int i=0;i<=n;i++)f[i][0]=i;
for(int j=1;j<=maxdep;j++)
for(int i=0;i+(1<<j)-1<=n;i++){
int to=i+(1<<(j-1));
int p1=f[i][j-1],p2=f[to][j-1];
if(sum[p1]<sum[p2])f[i][j]=p1;
else f[i][j]=p2;
}
}
il int qry(int l,int r){
if(l<0)l=0;
int k=Log[r-l+1],p1=f[r-(1<<k)+1][k],p2=f[l][k];
return sum[p1]<sum[p2]?p1:p2;
}
void solve(){
int l,r,p,val;
for(int i=L;i<=n;i++){
l=Max(0,i-R);r=Max(0,i-L);
p=qry(l,r);
val=sum[i]-sum[p];
q.push(node(l,r,p,val,i));
}
ll ans=0;RG node t;
for(int i=1;i<=K;i++){
t=q.top();q.pop();
ans+=t.val;
l=t.l;r=t.p-1;
if(l<=r){
p=qry(l,r);
val=sum[t.id]-sum[p];
q.push(node(l,r,p,val,t.id));
}
l=t.p+1;r=t.r;
if(l<=r){
p=qry(l,r);
val=sum[t.id]-sum[p];
q.push(node(l,r,p,val,t.id));
}
}
printf("%lld\n",ans);
}
void work()
{
scanf("%d%d%d%d",&n,&K,&L,&R);
maxdep=log(n)/log(2)+1;
for(RG int i=2;i<=n;i++)Log[i]=Log[i>>1]+1;
for(RG int i=1;i<=n;i++)scanf("%d",&a[i]),sum[i]=sum[i-1]+a[i];
priwork();
solve();
}
int main()
{
work();
return 0;
}