2023-10-14 10:30阅读: 5评论: 0推荐: 1

『结题记录』P2048 超级钢琴

RMQ 好题,但是思路贺的(悲)。思路挺好,怕忘了,写个题解罢,因为是贺的就不发博客园了。

天猫超市门

Description

n 个音符,每个音符的美妙度为 ai , 求出 k 个长度在 [l,r] 之间的连续的音符称之为和弦,使 k 个和弦的美妙度最大。

Solution

因为和弦是连续,考虑使用前缀和。

先思考求最大的和弦。暴力求每个区间和然后取最大?显然会 T 飞。可以考虑确定区间的起点 b ,用 b+l1b+r1 的最大前缀和 求出以 b 为起点的最大和弦。设 tb+l1b+r1 的最大前缀和的位置,即 t=maxb+l1ib+r1sumi 。最大和弦即为 max1bnl+1sum[t]sum[b1]。需要注意 b+r1>n的情况。

再考虑求 k 个和弦使美妙度最大。可以发现,最大美妙度可能包含若干个区间有重叠的和弦,所以每个和弦可以分裂成小和弦。考虑将每个和弦的 b,l,r,t 记录在优先队列中,取 k 次队头,队头出队,再将队头和弦可以分裂的小和弦入队。因为需要用到 t 所以要用两个 st 表, maxn 存最大前缀和, p 存最大前缀和的位置。 pmaxn 变化即可。

code

#include <iostream>
#include <queue>
#include <cmath>
using namespace std;
const int Max = 5e5+10;
int n,k,l,r;long long ans;
int h[Max];
struct Num{
    int b,l,r,t;
    bool operator <(Num i)const{
        return h[t]-h[b-1] < h[i.t]-h[i.b-1];
    }
};
priority_queue<Num>q;
  
struct ST{
    int maxn[19][Max],p[19][Max];
    inline void build(){
        for(int j = 1;j <= 19;j++){
            for(int i = 1;i+(1<<j)-1<=n;i++){
                if(maxn[j-1][i]>maxn[j-1][i+(1<<j-1)]){
                    maxn[j][i]=maxn[j-1][i];
                    p[j][i]=p[j-1][i];
                }
                else{
                    maxn[j][i]=maxn[j-1][i+(1<<j-1)];
                    p[j][i]=p[j-1][i+(1<<j-1)];
                }
            }
        }
    }
    inline int findT(int l,int r){ //找到b+l-1到b+r-1的最大前缀和的位置(t)
        int k=__lg(r-l+1);
        if(maxn[k][l]>maxn[k][r-(1<<k)+1]){
            return p[k][l];
        }
        else return p[k][r-(1<<k)+1];
    }
}st;

inline int read(){
    int num=0,fl=1;char c=getchar();
    while(c<'0'||c>'9'){
        if(c=='-') fl=-1;
        c=getchar();
    }
    while(c >='0'&&c <='9'){
        num=(num<<3)+(num<<1)+(c^48);
        c=getchar();
    }
    return num*fl;
}

signed main(){
    n=read(),k=read(),l=read(),r=read();
    for(int i = 1,a;i <= n;i++) a=read(),h[i]=h[i-1]+a,st.maxn[0][i]=h[i],st.p[0][i]=i;
    st.build();
    for(int i = 1;i+l-1 <= n;i++){
        Num maxn;
        maxn.t=st.findT(i+l-1,min(i+r-1,n));
        maxn.b=i,maxn.l=i+l-1,maxn.r=min(i+r-1,n);
        q.push(maxn);
    }
    while(k--){
        Num to=q.top();
        ans+=h[to.t]-h[to.b-1];
        q.pop();
        Num x;
        if(to.l < to.t){//分裂
            x.b=to.b;x.l=to.l;x.r=to.t-1;
            x.t=st.findT(to.l,to.t-1);
            q.push(x);
        }
        if(to.t < to.r){
            x.b=to.b;x.l=to.t+1;x.r=to.r;
            x.t=st.findT(to.t+1,to.r);
            q.push(x);
        }
    }
    printf("%lld",ans);
    return 0;  
}

harvest

学会了处理区间和的新方式

学会了记录 st 表最值位置的方式

posted @   tkt  阅读(5)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起