超级钢琴题解
超级钢琴题解
这是一道RMQ的好题,再加上大根堆,对于刚学过rmq的人,这道题非常棒。~以上是我的一些废话~
解释
首先很容易想到一个贪心的策略,每一次选择美妙度最高的超级弦,选取k次即为最优解,问题在于求出每次美妙度最高的超级弦,区间最大值那不是rmq么,但是rmq维护的区间最大值是单点的,可是这道题确实一个区间和,甚至是有限制的区间和,即区间长度不确定,但却必须在一个范围内,所以我们进行转换,使用前缀和进行优化,我们发现每个超级弦的右端点虽然在变化,但是相对应的左端点并不会发生变化,那么,其实每个超级弦的最大值都可以用右端点的前缀和代替,我们用右端点前缀和的最大值减去左端点前缀和代表左端点权值,然后放入大根堆,每次取一次,取k次即可。注意:每次取完必须将下限到该端点的最大值和该端点到上限的最大值加入堆中
代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int MN=6e5+100;
int n,k,l,r;
int s[MN],maxn[MN][22],post[MN][22],lg[MN];
void init(){
for(int j=1;(1<<j)<=n;++j){
for(int i=1;i+(1<<j)-1<=n;++i){
if(maxn[i][j-1]>maxn[i+(1<<(j-1))][j-1]){
maxn[i][j]=maxn[i][j-1];
post[i][j]=post[i][j-1];
}
else{
maxn[i][j]=maxn[i+(1<<(j-1))][j-1];
post[i][j]=post[i+(1<<(j-1))][j-1];
}
}
}
}
int ans=0;
struct node{
int l,r1,r2,r,sum;
bool operator<(node a)const{
return sum<a.sum;
}
};
priority_queue<node>q;
inline void add(int r,int l,int r1,int r2){
node t;
t.r=r;
t.l=l;
t.r1=r1;
t.r2=r2;
t.sum=s[r]-s[l-1];
q.push(t);
}
int query(int l,int r){
int k=lg[r-l+1];
int sit=0;
if(maxn[l][k]>maxn[r-(1<<k)+1][k]){
sit=post[l][k];
}
else{
sit=post[r-(1<<k)+1][k];
}
return sit;
}
signed main(){
freopen("piano.in","r",stdin);
freopen("piano.out","w",stdout);
scanf("%lld%lld%lld%lld",&n,&k,&l,&r);
lg[0]=-1;
for(int i=1,x;i<=n;++i){
scanf("%lld",&x);
s[i]=s[i-1]+x;
maxn[i][0]=s[i];
post[i][0]=i;
lg[i]=lg[i>>1]+1;
}
init();
for(int i=1;i+l-1<=n;++i){
add(query(i+l-1,min(i+r-1,n)),i,i+l-1,min(i+r-1,n));
}
while(k--){
int r=q.top().r,l=q.top().l,r1=q.top().r1,r2=q.top().r2,sum=q.top().sum;
ans+=sum;
q.pop();
if(r>r1)add(query(r1,r-1),l,r1,r-1);//注意
if(r<r2)add(query(r+1,r2),l,r+1,r2);
}
printf("%lld",ans);
return 0;
}

浙公网安备 33010602011771号