题意
已知一个数组a,我们可以选择它一个下标范围[l,r]进行升序排序,得到数组a2。
a=[6,7,3,4,4,6,5], 选定l=2,r=5,那么排序后的a2=[6,3,4,4,7,6,5]。
现给定数组a和a2,求出原来的下标范围l,r。
如果存在多组解,选择下标范围最长的;如果下标范围最长的有多组解,输出任意一解。
思路
由于排序后,数组的相对值发生了变化。
我们可以找出左边第一个取值不同的边界(a[l]!=a2[l]),以及右边第一个取值不同的边界(a[r]!=a2[r])。然后根据有序性,扩展左、右边界即可。
特判:我们需要考虑a和a2完全相等的场景,此时题目转换为最长有序 连续子数组。
代码
#include<iostream>
#include<vector>
#include<set>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn = 200010;
int n;
int a[maxn];
int b[maxn];
vector<int> solve() {
scanf("%d", &n);
for (int i = 0; i < n; ++i) {
scanf("%d", &a[i]);
}
for (int i = 0; i < n; ++i) {
scanf("%d", &b[i]);
}
int l = 0, r = n - 1;
while (l < n && a[l] == b[l]) {
++l;
}
while (r >= 0 && a[r] == b[r]) {
--r;
}
if (l == n) { // all equal
int len = 1, mx = 0, st = 0, ed = 0;
vector<int> ans;
for (int i = 1; i < n; ++i) {
if (a[i] >= a[i-1]) {
++len;
ed = i;
} else {
len = 1;
st = ed = i;
}
if (mx < len) {
mx = len;
ans = {st, ed};
}
}
return ans;
}
// update leftest and rightest bound.
while (l && b[l] >= b[l-1]) {
--l;
}
while (r + 1 < n && b[r] <= b[r+1]) {
++r;
}
return {l, r};
}
int main() {
int t;
scanf("%d", &t);
while (t--) {
auto ans = solve();
printf("%d %d\n", ans[0] + 1, ans[1] + 1);
}
}