题解:CF2205D Simons and Beating Peaks
注意到,数组的最大值必须置于两端。否则,我们必须将其左侧或右侧的所有元素全部删除,然后得到一个新的子数组。每次都会导致区间的缩小,因而可以递归求解。
具体地,对于区间
- 删去下标在
l \sim x - 1 内的所有元素,区间缩小为[x + 1, r] 。 - 删去下标在
x + 1 \sim r 内的所有元素,区间缩小为[l, x - 1] 。
因此,递推式为
如果暴力求解区间最大值,那么平均复杂度为
故考虑用 ST 表维护区间最大值,递归过程可优化至
时间复杂度
#include <bits/stdc++.h>
using namespace std;
int t, n, a[500001], pos[500001], st[500001][19];
int query(int l, int r) {
int j = __lg(r - l + 1);
return max(st[l][j], st[r-(1<<j)+1][j]);
}
int solve(int l, int r) {
if (r - l <= 1) return 0;
int x = pos[query(l,r)];
return min(r - x + solve(l, x - 1), x - l + solve(x + 1, r));
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> t;
while (t--) {
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> a[i];
pos[a[i]] = i;
}
for (int i = 1; i <= n; i++) st[i][0] = a[i];
for (int j = 1; (1 << j) <= n; j++)
for (int i = 1; i + (1 << j) - 1 <= n; i++)
st[i][j] = max(st[i][j-1], st[i+(1<<(j-1))][j-1]);
cout << solve(1, n) << '\n';
}
return 0;
}