*题解: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;
} 
posted @ 2026-10-05 12:23  yutar  阅读(6)  评论(0)    收藏  举报