Right Maximum
题面
You are given an array consisting of integers.
While the array is not empty, an operation is performed consisting of two steps:
- first, the maximum element in the array is chosen (if there are multiple maximum elements, the rightmost maximum is chosen);
- then, all elements after the chosen element, including it, are removed from the array.
Your task is to calculate the number operations that will be performed before the array becomes empty.
Input
The first line contains one integer () — the number of test cases.
Each test case consists of two lines:
- the first line contains one integer ();
- the second line contains integers ().
Additional constraint on the input: the sum of over all test cases does not exceed .
Output
For each test case, print one integer — the number of operations that will be performed.
思路
题目很简单,我们只需要规定一个右边界 ,让这个右边界等于每一次找到的最大值所在的索引即可,每一次查找就算一次次数,最终得到想要的结果
但是,如果直接暴力写法,会导致 TLE
所以,我们采用下面的方法:
先预处理一个数组 ,其中 表示前缀区间 内最右侧最大值的下标;这样,转移时只要维护当前最大值 和位置即可
如果 ,就更新 且
否则继承前一个结果,令
这样一遍扫描就能把所有前缀的答案算完,预处理复杂度是
然后模拟操作时不再重新扫描前缀找最大值,而是设当前剩余数组右边界为 ,对应区间是 ;那么,本次应选择的下标就是
执行删除后新的右边界直接跳到
每跳一次就代表一次操作,答案加
原本暴力做法每次都要在前缀内找最大值,最坏复杂度是
优化后是一次线性预处理加若干次 跳转,总复杂度为
结合数据范围 ,线性做法可以稳定通过
代码
#include <bits/stdc++.h>
using namespace std;
int main() { int t; cin >> t; while (t --) { int n; cin >> n; vector<int> a(n); for (int i = 0; i < n; i ++) cin >> a[i];
vector<int> pos(n); int mx = a[0]; pos[0] = 0; for (int i = 1; i < n; i ++) { if (a[i] >= mx) { mx = a[i]; pos[i] = i; } else { pos[i] = pos[i - 1]; } }
int r = n; int ans = 0; while (r > 0) { ans ++; r = pos[r - 1]; } cout << ans << endl; }}