题解:[NOI2010] 超级钢琴

题目传送门

题意分析

求前 \(k\) 大的答案之和,考虑多路归并。

\(a\) 的前缀和为 \(\textit{pre}\)

设计状态 \((s,l,r)\),表示区间左端点为 \(s\),右端点在 \([l,r]\)

则有:

\[\textit{ans}(s,l,r)=\max_{i=l}^r\textit{pre}_i-\textit{pre}_{s-1} \]

因此只需要求 \([l,r]\) 内的 \(\textit{pre}_i\) 最大值,ST 表即可 \(\mathcal O(1)\) 维护。

\(L,R\) 的限制即:

\[i+L-1\leq l\leq r\leq\min(i+R-1,n) \]

因为左端点 \(s\) 一定时,可以确定右端点在 \([l,r]\) 内的最优右端点 \(p\)。之后把状态分裂成 \((s,l,p-1),(s,p+1,r)\)。这两个状态一定不会比 \((s,l,r)\) 更优。因此对于每一个 \(i\) 求出初始状态,之后丢进大根堆里多路归并即可。

实际是线上还需要注意要保证 \(l\leq p-1,p+1\leq r\)。(因为分裂后区间最大长度不会增加,最小长度不会减小,所以不用考虑 \(L,R\) 的限制。)

时间复杂度 \(\mathcal O((n+k)\log n)\)

AC 代码

//#include<bits/stdc++.h>
#include<algorithm> 
#include<iostream>
#include<cstring>
#include<iomanip>
#include<cstdio>
#include<string>
#include<vector>
#include<cmath>
#include<ctime>
#include<deque>
#include<queue>
#include<stack>
#include<list>
using namespace std;
typedef long long ll;
constexpr const int N=5e5,K=5e5;
int n,k,L,R,a[N+1];
struct ST{
	pair<int,int> st[N+1][__lg(N+1)+1];
	
	void build(int a[],int n){
		for(int i=1;i<=n;i++){
			st[i][0]={a[i],i};
		}
		for(int i=1;(1<<i)<=n;i++){
			for(int x=1;x+(1<<i)-1<=n;x++){
				st[x][i]=max(st[x][i-1],st[x+(1<<i-1)][i-1]);
			}
		}
	}
	pair<int,int> query(int l,int r){
		int s=__lg(r-l+1);
		return max(st[l][s],st[r-(1<<s)+1][s]);
	}
}st;
struct node{
	int s,l,r;
	
	int value(){
		return st.query(l,r).first-a[s-1];
	}
	int pos(){
		return st.query(l,r).second;
	}
};
bool operator <(node a,node b){
	return a.value()<b.value();
}
int main(){
	/*freopen("test.in","r",stdin);
	freopen("test.out","w",stdout);*/
	
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	
	cin>>n>>k>>L>>R;
	for(int i=1;i<=n;i++){
		cin>>a[i];
		a[i]+=a[i-1];
	}
	st.build(a,n);
	priority_queue<node>q;
	for(int i=1;i<=n-L+1;i++){
		q.push({i,i+L-1,min(i+R-1,n)});
	}
	ll ans=0;
	while(q.size()&&k){
		node x=q.top();
		q.pop();
		ans+=x.value();
		k--;
		int p=x.pos();
		if(L<=p-x.s&&x.l<=p-1){
			q.push({x.s,x.l,p-1});
		}
		if(p+1<=x.r){
			q.push({x.s,p+1,x.r});
		}
	}
	cout<<ans<<'\n';
	
	cout.flush(); 
	
	/*fclose(stdin);
	fclose(stdout);*/
	return 0;
}
posted @ 2026-07-08 21:23  TH911  阅读(4)  评论(0)    收藏  举报