*题解:P16902 [CCO 2026] Melborp
解析
先考虑排列的情况。记 \(l_i,r_i\) 分别为 \(a_i\) 左边和右边最后一个 \(\ge a_i\) 的位置,那么 \(b_i\) 就为 \((i - l_i + 1)(r_i - i + 1)\)。而对于 \(a_i = \min_{j=1}^n a_j\),\(b_i = (i - 1 + 1)(n - i + 1)\)。也就是说我们可以扫一遍找到这个 \(i\),给 \(a_i\) 赋上当前可赋的最小值。又由于 \(a_i\) 是最小的,所以对于 \(j < i\),有 \(r_j < i\),对于 \(k > i\),有 \(l_k > i\)。也就是说,可以以 \(i\) 为分界点将原问题划分成 \([1,i - 1],[i + 1,n]\) 的子问题,递归下去求解。找 \(i\) 我们考虑暴力扫,直接从左往右或从右往左扫复杂度是错的,但从两边往中间扫复杂度就对了,我们尝试分析一下:
首先我们有:
\(T(n) = T(i) + T(n - i) + O(\min(i,n - i))\)
由于每个问题是 \(\min(i,n - i)\) 在做贡献,那么做贡献的那一段必定不大于原区间长度的一半,反过来,父问题规模至少为做贡献的那一段规模的两倍,这类似于启发式合并,故 \(T(n) = O(n\log n)\)。
现在考虑非排列,也即有相等元素的情况。
尝试仍然用原来的方法,发现设上一层找到的最小值为 \(a_j\),分治下来的当前处理区间为 \([L,R]\),那么对于 \(a_i = a_j\) 必定会有 \(b_i > (i - L + 1)(R - i + 1)\) 的情况,此时将 \(a_i\) 赋值为 \(a_j\) 再向下递归即可。那么有没有可能 \([L,R]\) 内存在比 \(a_i\) 更小的值使得其也满足大于关系呢?不可能。考虑 \(a_k = a_i\) 且 \(a_k\) 为值为 \(a_i\) 的最早被选中为最小值的元素。显然其被选中时所正在处理的区间包含 \([L,R]\),而其为第一次被选中,就说明 \([l_k,r_k]\) 就为当时处理的区间,根据 \(l_k,r_k\) 的定义,区间内自然不存在 \(< a_k\) 的值。
时间复杂度 \(O(n\log n)\)。
没想到的地方
考虑最小值。
总结
对于最值相关问题,可以尝试从整体最值入手。
代码
/*
以下为自主做题时的思路:
考虑 b[1]
其说明 a[1] 是 a[1,b[1]] 的最小值
a[2,b[1]] >= a[1]
a[b[1] + 1] < a[1]
对于 [2,b[1]] 之间的数,其左边必定可以扩展到 1,这部分有 i 的贡献,
5 4 1 9 4 1
若均互不相同,可扩展到的左端点
1 2 3 3 5 6
*/
#include<bits/stdc++.h>
using namespace std;
const int N = (int)5e6 + 5;
typedef long long ll;
ll a[N],b[N];
int now,cnt;
void sol(int l,int r,int v){
if(l > r) return;
cnt++;
int i = l,j = r;
// cerr<<l<<" "<<r<<" "<<v<<'\n';
while(1){
ll x = 1ll * (i - l + 1) * (r - i + 1);
if(b[i] == x){
now++;
a[i] = now;
int val = now;
sol(l,i - 1,val),sol(i + 1,r,val);
break;
}else if(b[i] > x){
a[i] = v;
sol(l,i - 1,v),sol(i + 1,r,v);
break;
}
ll y = 1ll * (j - l + 1) * (r - j + 1);
if(b[j] == y){
now++;
a[j] = now;
int val = now;
sol(l,j - 1,val),sol(j + 1,r,val);
break;
}else if(b[j] > y){
a[j] = v;
sol(l,j - 1,v),sol(j + 1,r,v);
break;
}
i++,j--;
}
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
// freopen("ex_subarray9.in","r",stdin);
// freopen("subarray.in","r",stdin);
// freopen("subarray.out","w",stdout);
int n;
cin>>n;
for(int i=1;i<=n;i++){
cin>>b[i];
}
sol(1,n,0);
for(int i=1;i<=n;i++){
cout<<a[i]<<" ";
}
return 0;
}

浙公网安备 33010602011771号