[NOI2010]:超级钢琴

传送门
这个题有趣。。。
巧妙地利用st表和堆
首先最暴力的我就不说了
第二个暴力就是主席树+堆,预计得分70~80,时间 O(klog2n)
std是用堆存储可能的区间,然后用st表查询区间最小值
因为其实如果知道区间右端点,再处理个前缀和s
那么就只要查询区间最小值就可以了,可以st表O(1)做
代码:

#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<queue>
#define mp(a,b,c,d) make_pair(make_pair(a,b),make_pair(c,d))
#define min(a,b) s[a]>s[b]?b:a
using namespace std;
inline int read(){
    int x=0,f=1;char ch=0;
    while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
    if(ch=='-')f=-1,ch=getchar();
    while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
    return x*f;
}
typedef pair<int,int> pii;
const int N=5e5+5;
int n,k,L,R;
int a[N],mn[19][N],Log[N],s[N];
long long ans=0;
priority_queue<pair<pii,pii> > q;
inline int query(int l,int r){
    if(l>r)return -1;
    int k=Log[r-l+1];
    return min(mn[k][l],mn[k][r-(1<<k)+1]);
}
int main(){
    n=read();k=read();L=read();R=read();Log[0]=-1;
    for(int i=1;i<=n;++i)a[i]=read(),mn[0][i]=i,Log[i]=Log[i>>1]+1,s[i]=s[i-1]+a[i];
    for(int k=1;k<=Log[n];++k)for(int i=0;i+(1<<k)-1<=n;++i)mn[k][i]=min(mn[k-1][i],mn[k-1][i+(1<<(k-1))]);
    for(int i=L;i<=n;++i)q.push(mp(s[i]-s[query(max(0,i-R),i-L)],i,max(0,i-R),i-L));
    int x,a,b,c,d,y;
    for(int i=1;i<=k;++i){
        pii p1=q.top().first,p2=q.top().second;
        ans+=p1.first;x=p1.second;a=p2.first;b=p2.second;y=query(a,b);q.pop();
        c=query(a,y-1);d=query(y+1,b);
        if(c!=-1)q.push(mp(s[x]-s[c],x,a,y-1));
        if(d!=-1)q.push(mp(s[x]-s[d],x,y+1,b));
    }
    printf("%lld",ans);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值