2024 CCPC 河北省大学生程序设计竞赛 B题思路分享(单调栈,启发式分裂,ST表)

题意概述

给定一个 \(n\) 个元素的数组 \(a\),定义区间 \([l,r]\) 的价值为:

\[\max(a_l,a_{l+1},\cdots,a_r) \times \min(a_l,a_{l+1},\cdots,a_r) \times (r-l+1) \]

求第 \(k\) 大的区间价值。

\(1\le n \le 5\times 10^4\)

思路

求第 \(k\) 大,考虑二分区间价值 \(V\),找到第一个满足区间价值严格大于 \(V\) 的区间数量 \(\ge k-1\)\(V\) 即可。

考虑固定最小值,可以通过单调栈预处理出 \(a_i\) 的支配区间,启发式地枚举长度较短的那一侧,二分另一侧,统计区间价值严格大于 \(V\) 的区间数量。使用 \(st\) 表处理区间最大值的查询。

时间复杂度 \(\mathcal{O}(n\log^3 n)\)

代码

//author:kzssCCC

#include <bits/stdc++.h>
using namespace std;
using ll = long long;


void solve(){
	int n;
	ll k;
	cin >> n >> k;

	vector<int> a(n+1);
	for (int i=1;i<=n;i++){
		cin >> a[i];
	}

	vector<int> left(n+1),right(n+1);
	stack<int> stk;

	for (int i=1;i<=n;i++){
		whil (!stk.empty() && a[i]<=a[stk.top()]){
			stk.pop();
		}

		left[i] = stk.empty()?0:stk.top();
		stk.push(i);
	}

	while (!stk.empty()){
		stk.pop();
	}

	for (int i=n;i>=1;i--){
		while (!stk.empty() && a[i]<a[stk.top()]){
			stk.pop();
		}

		right[i] = stk.empty()?n+1:stk.top();
		stk.push(i);
	}

	vector<int> lg(n+1);
	for (int i=2;i<=n;i++){
		lg[i] = lg[i>>1]+1;
	}

	int K = lg[n];
	vector<vector<int>> dp(n+1,vector<int>(K+1));
	for (int i=1;i<=n;i++){
		dp[i][0] = a[i];
	}

	for (int k=1;k<=K;k++){
		int len = 1<<k;
		int half = len>>1;

		for (int i=1;i+len-1<=n;i++){
			dp[i][k] = max(dp[i][k-1],dp[i+half][k-1]);
		}
	}

	auto query = [&](int l,int r){
		int len = r-l+1;
		int k = lg[len];

		return max(dp[l][k],dp[r-(1<<k)+1][k]);
	};

	auto check = [&](ll v){
		ll cnt = 0;

		for (int i=1;i<=n;i++){
			if (i-left[i]<=right[i]-i){
				for (int j=left[i]+1;j<=i;j++){

					int l=i,r=right[i]-1;
					while (l<=r){
						int mid = l+r >> 1;

						if ((ll)a[i]*query(j,mid)*(mid-j+1)>v){
							r = mid-1;
						}
						else{
							l = mid+1;
						}
					}

					cnt += right[i]-l;
					if (cnt>=k) return true;
				}
			}	
			else{
				for (int j=i;j<right[i];j++){
					int l=left[i]+1,r=i;
					while (l<=r){
						int mid = l+r >> 1;
						
						if ((ll)a[i]*query(mid,j)*(j-mid+1)>v){
							l = mid+1;
						}			
						else{
							r = mid-1;
						}			
					}

					cnt += r-left[i];
					if (cnt>=k) return true;
				}
			}
		}

		return cnt>=k;
	};

	ll mx = *max_element(a.begin()+1,a.end());

	ll l=1,r=mx*mx*n;
	while (l<=r){
		ll mid = l+(r-l>>1);
		if (check(mid)){
			l = mid+1;
		}
		else{
			r = mid-1;
		}
	}

	cout << l << '\n';
}

int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	
	int t = 1;
	// cin >> t;
	while (t--) solve();

	return 0;
}
posted @ 2026-05-16 16:21  kzssCCC  阅读(15)  评论(0)    收藏  举报