题意:有一个长度为 n 的全排列数组 a。任意两个索引 i, j,如果在 ai 到 aj 中 ai 和 aj 分别是最大最小值或者是最小最大值,那么 i 和 j 有一条距离 1 的无向边,求1 到 n 的最短距离。
思路:用线段树找到区间的最大值和最小值。那么这个最大值和最小值一定会有一条边,并且他们之间的数也不会和外面的数有边,那么只要再去递归的处理剩余的部分,就能计算出答案。
代码:
#include<bits/stdc++.h>
#define pb push_back
using namespace std;
typedef long long ll;
typedef pair<int, int> PII;
const int N = 3e5 + 10, P = 1e9 + 7, mod = 998244353;
int a[N] pos[N];
int maxn[N << 2], minn[N << 2];
int n;
void pushup(int x){
maxn[x] = max(maxn[x << 1], maxn[x << 1 | 1]);
minn[x] = min(minn[x << 1], minn[x << 1 | 1]);
}
void build(int l, int r, int x) {
if(l == r){
minn[x] = maxn[x] = a[l];
return;
}
int mid = l + r >> 1;
build(l, mid, x << 1);
build(mid + 1, r, x << 1 | 1);
pushup(x);
}
int qmax(int l, int r, int x, int ql, int qr) {
if(l >= ql && r <= qr) return maxn[x];
int mid = l + r >> 1;
int res = 0;
if(ql <= mid) res = max(res, qmax(l, mid, x << 1, ql, qr));
if(qr > mid) res = max(res, qmax(mid + 1, r, x << 1 | 1, ql, qr));
return res;
}
int qmin(int l, int r, int x, int ql, int qr) {
if(l >= ql && r <= qr) return minn[x];
int mid = l + r >> 1;
int res = n + 1;
if(ql <= mid) res = min(res, qmin(l, mid, x << 1, ql, qr));
if(qr > mid) res = min(res, qmin(mid + 1, r, x << 1 | 1, ql, qr));
return res;
}
int ans(int l, int r){
if(l + 1 == r) return 1;
if(l >= r) return 0;
int ma = qmax(1, n, 1, l, r);
int mi = qmin(1, n, 1, l, r);
int pl = pos[ma], pr = pos[mi];
if(pl > pr) swap(pl, pr);
return ans(l, pl) + ans(pr, r) + 1;
}
void solve(){
cin >> n;
for(int i = 1; i <= n; i++) cin >> a[i], pos[a[i]] = i;
build(1, n, 1);
cout << ans(1, n) << endl;
}
int main(){
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int t;
cin >> t;
while(t--) {
solve();
}
return 0;
}