题目
思路来源
灵茶群、propane代码
题解
线段树+set或者multiset均可
感觉pyy这个代码写的还是挺巧妙的,也避免了使用线段树求区间最值
注意到序列由最多三段拼接而成,
一段增序(非严格),一段存在逆序的部分,一段增序(非严格)
贴一下灵茶群群友的图,
对于存在逆序的部分,记录下来这一段最左最右的位置,求出这一段最小值和最大值
前面那段的最大值只能小于等于这一段最小值,后面那段的最小值只能大于等于这段最大值
分别二分即可,可以线段树二分,也可以在multiset上二分
代码
#include<iostream>
#include<cstring>
#include<vector>
#include<set>
#include<algorithm>
using namespace std;
using LL = long long;
int main(){
#ifdef LOCAL
freopen("data.in", "r", stdin);
freopen("data.out", "w", stdout);
#endif
cin.tie(0);
cout.tie(0);
ios::sync_with_stdio(0);
int T;
cin >> T;
while(T--){
int n;
cin >> n;
vector<int> a(n + 1);
for(int i = 1; i <= n; i++) cin >> a[i];
multiset<int> s, s1, s2;
for(int i = 1; i + 1 <= n; i++){
if (a[i] > a[i + 1]){
s.insert(i);
s1.insert(a[i]);
s2.insert(a[i + 1]);
}
}
auto add = [&](int x){
if (x - 1 >= 1 and a[x - 1] > a[x]){
s.insert(x - 1);
s1.insert(a[x - 1]);
s2.insert(a[x]);
}
if (x + 1 <= n and a[x] > a[x + 1]){
s.insert(x);
s1.insert(a[x]);
s2.insert(a[x + 1]);
}
};
auto del = [&](int x){
if (x - 1 >= 1 and a[x - 1] > a[x]){
s.erase(x - 1);
s1.erase(s1.find(a[x - 1]));
s2.erase(s2.find(a[x]));
}
if (x + 1 <= n and a[x] > a[x + 1]){
s.erase(x);
s1.erase(s1.find(a[x]));
s2.erase(s2.find(a[x + 1]));
}
};
auto get = [&](){
if (s.empty()){
cout << -1 << ' ' << -1 << '\n';
return;
}
int L = *s.begin();
int R = *s.rbegin() + 1;
{
int mn = *s2.begin();
cout << int(upper_bound(a.begin() + 1, a.begin() + L + 1, mn) - a.begin()) << ' ';
}
{
int mx = *s1.rbegin();
cout << int(lower_bound(a.begin() + R, a.end(), mx) - a.begin() - 1) << '\n';
}
};
get();
int q;
cin >> q;
while(q--){
int x, y;
cin >> x >> y;
del(x);
a[x] = y;
add(x);
get();
}
}
}