题目传送门:Codeforces-1696 D: Permutation Graph
给定一个 1 ⋯ n 1 \cdots n 1⋯n 的排列,对于一个连续的区间 a i ⋯ a j a_i \cdots a_j ai⋯aj,若 a i a_i ai 是这段区间的最小/最大值,同时, a j a_j aj 也是这段区间的最小/最大值,那么我们可以在 i , j i, j i,j 结点上连一条无向边。问这样构造出的图,从 1 1 1 到 n n n 的最短路径。
首先暴力是肯定不行的,因为若是一个单调的数组,能连的边是
n
2
n^2
n2 级别的。
这道题表面上是最短路径,实际上是考察必经结点。现在假设
a
1
⋯
a
n
a_1 \cdots a_n
a1⋯an 的最大值在
a
2
⋯
a
n
−
1
a_2 \cdots a_{n-1}
a2⋯an−1 中,那么一个显然的结论是,这种情况下我们是无法绕过最大值从
1
1
1 达到
n
n
n 的,最小值同理。那么其中的最大值就变成了一个必经的结点
k
k
k。那么这样的必经之路有多少呢,显然
[
1
,
k
)
[1,k)
[1,k) 中的最小值,
(
k
,
n
]
(k,n]
(k,n] 中的最小值都是必经之路,之后我们递归向左向右寻找这种必经之路,就能形成
1
1
1···最小-最大-最小···
n
n
n 这样的路径,显然,只存在必经之路的路径是最短的。
#include <bits/stdc++.h>
using namespace std;
const int maxn = 6e5 + 7;
int log_2[maxn];
struct P {
int v, id;
}mx[maxn][18], mn[maxn][18];
void setST(const vector<int>& a) {
log_2[0] = -1;
for(int i=0; i<a.size(); ++i) {
mx[i][0].v = mn[i][0].v = a[i];
mx[i][0].id = mn[i][0].id = i;
log_2[i+1] = (((i+1)&i) == 0)?log_2[i]+1: log_2[i];
}
for(int j=1; j<18; ++j)
for(int i=0; i<a.size(); ++i) {
mx[i][j] = (mx[i][j-1].v < mx[i+(1<<(j-1))][j-1].v)? mx[i+(1<<(j-1))][j-1]:mx[i][j-1];
mn[i][j] = (mn[i][j-1].v > mn[i+(1<<(j-1))][j-1].v)? mn[i+(1<<(j-1))][j-1]:mn[i][j-1];
}
}
int query_mx(int l, int r) {
int k = log_2[r - l + 1];
if(mx[l][k].v < mx[r - (1<<k) + 1][k].v)
return mx[r - (1<<k) + 1][k].id;
else return mx[l][k].id;
}
int query_mn(int l, int r) {
int k = log_2[r - l + 1];
if(mn[l][k].v > mn[r - (1<<k) + 1][k].v)
return mn[r - (1<<k) + 1][k].id;
else return mn[l][k].id;
}
void dfs(int l, int r, bool cur, bool dir, vector<int>& a, vector<int>& ans) {
if(dir) {
int idx = cur?query_mn(l, r):query_mx(l, r);
if(l < idx) dfs(l, idx, cur^1, dir, a, ans);
ans.emplace_back(idx);
} else {
int idx = cur?query_mn(l, r):query_mx(l, r);
if(idx < r) dfs(idx, r, cur^1, dir, a, ans);
ans.emplace_back(idx);
}
}
int main() {
int T, n;
cin >> T;
while(T--) {
cin >> n;
vector<int> a(n);
for(int& i : a) cin >> i;
if(n == 1) {
cout << 0 << endl; continue;
} else if(n == 2) {
cout << 1 << endl; continue;
}
setST(a);
vector<int> ans;
int idx = query_mx(0, n - 1); ans.emplace_back(idx);
if(0 < idx) dfs(0, idx, true, true, a, ans);
if(idx < n - 1) dfs(idx, n - 1, true, false, a, ans);
cout << ans.size() - 1 << endl;
}
return 0;
}