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;
}

浙公网安备 33010602011771号