bzoj2006: [NOI2010]超级钢琴

2 篇文章 0 订阅

题目
题解

Description

题目看着老长,实际上就是求满足区间长度 ∈ [ l , r ] ∈[l,r] [l,r]的前 k k k大区间和的和

Solution

对于任意区间右端点 i i i,其左端点取值在 l l l, r r r之间,若左端点为 m m m,则 v v v m a x ( s u m [ i ] − s u m [ m − 1 ] ) max(sum[i]-sum[m-1]) max(sum[i]sum[m1]),显然这里 i i i是不变的,所以可以用 s t st st表查询 m m m的位置,然后计算v。
现将所有右端点扫一遍,然后扔到堆里面,堆中节点记录的是决策,即右端点 i i i,左端点区间,优先级由 v v v决定
然后取出堆顶, v v v加到 a n s ans ans里去,分裂 [ l , r ] [l,r] [l,r] [ l , m − 1 ] [l,m-1] [l,m1] [ m + 1 , r ] [m+1,r] [m+1,r] R M Q RMQ RMQ出新的 v v v,再将分裂后的区间加到堆里
重复 k k k

Code

#include<bits/stdc++.h>
using namespace std;
const int N=500002;
struct node{
	int i,l,m,r,v;
	friend bool operator <(node a,node b){return a.v<b.v;}
}t;
priority_queue<node>q;
int lg[N],L,R,i,j,n,k,a[N],tmp,st[N][20],l,r,m;
long long ans;
inline char gc(){
	static char buf[100000],*p1=buf,*p2=buf;
	return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline int rd(){
	int x=0,fl=1;char ch=gc();
	for (;ch<48||ch>57;ch=gc())if(ch=='-')fl=-1;
	for (;48<=ch&&ch<=57;ch=gc())x=(x<<3)+(x<<1)+(ch^48);
	return x*fl;
}
int get(int x,int y){
	x--;y--;
	int l=lg[y-x+1];
	if (a[st[x][l]]<a[st[y-(1<<l)+1][l]]) return st[x][l]+1;
	return st[y-(1<<l)+1][l]+1;
}
int main(){
	n=rd();k=rd();L=rd();R=rd();
	lg[0]=-1;
	for (i=1;i<=n;i++) a[i]=rd()+a[i-1],lg[i]=lg[i>>1]+1,st[i][0]=i;
	for (j=1;j<=lg[n];j++)
		for (i=0;i+(1<<j)-1<=n;i++)
			if (a[st[i][j-1]]<a[st[i+(1<<j-1)][j-1]]) st[i][j]=st[i][j-1];
			else st[i][j]=st[i+(1<<j-1)][j-1];
	for (i=1;i<=n;i++){
		l=max(i-R+1,1);r=max(i-L+1,1);
		if (i-l+1<L) continue;
		m=get(l,r);
		q.push((node){i,l,m,r,a[i]-a[m-1]});
	}
	while (k--){
		t=q.top();q.pop();
		ans+=t.v;
		l=t.l;r=t.r;m=t.m;
		if (l<m){
			tmp=get(l,m-1);
			q.push((node){t.i,l,tmp,m-1,a[t.i]-a[tmp-1]});
		}
		if (m<r){
			tmp=get(m+1,r);
			q.push((node){t.i,m+1,tmp,r,a[t.i]-a[tmp-1]});
		}
	}
	printf("%lld",ans);
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值