Codeforces Global Round 21 - D. Permutation Graph
分治
[Problem - D - Codeforces](https://codeforces.com/contest/1695/problem/D2)
题意
给出一个长度为 \(n(1<=2.5*10^5)\) 的排列 \(a_i\),如果对于 \(1<=l<r<=n\), \(a_l,a_r\) 都是 \([l,r]\) 区间的最值(即一个是最小值,一个是最大值),则 \(l,r\) 可以连一条长度为 1 的边
求 1 号结点 到 n 号结点的最短路
思路
- 假设 \(a_L=1,a_R=n\) (相反亦可),则从 1 号点到 n 号点一定要经过 L,R 两点,不能直接越过它们;L 号点到 R 号点可以一次通过,因此 \(solve(1,n)=solve(1,L)+1+solve(R,n)\)
- 继续递归下去,找到 \([1,L]\) 的最大值与最小值,继续递归。。。
- 可以用 st表或预处理前后缀最值来快速找到前缀(后缀)最值出现的下标
复杂度 \(O(n)\)
代码
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#include <cmath>
using namespace std;
#define endl "\n"
typedef long long ll;
typedef pair<int, int> PII;
const int N = 2.5e5 + 10;
const int INF = 2e9;
int n;
int a[N];
int l_min[N], l_max[N], r_min[N], r_max[N];
int f[N];
void presolve()
{
int minn = INF, maxn = -INF;
for (int i = 1; i <= n; i++)
{
if (a[i] < minn)
{
minn = a[i];
l_min[i] = i;
}
else
l_min[i] = l_min[i-1];
if (a[i] > maxn)
{
maxn = a[i];
l_max[i] = i;
}
else
l_max[i] = l_max[i-1];
}
minn = INF, maxn = -INF;
for (int i = n; i >= 1; i--)
{
if (a[i] < minn)
{
minn = a[i];
r_min[i] = i;
}
else
r_min[i] = r_min[i+1];
if (a[i] > maxn)
{
maxn = a[i];
r_max[i] = i;
}
else
r_max[i] = r_max[i+1];
}
}
int solve(int l, int r)
{
if (l == r)
return 0;
if (l != 1 && r != n)
return 1;
int L, R;
if (l == 1)
{
L = min(l_min[r], l_max[r]);
R = max(l_min[r], l_max[r]);
}
else
{
L = min(r_min[l], r_max[l]);
R = max(r_min[l], r_max[l]);
}
if (l == L && r == R)
return 1;
return solve(l, L) + 1 + solve(R, r);
}
int main()
{
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int T;
cin >> T;
while(T--)
{
cin >> n;
for (int i = 1; i <= n; i++)
cin >> a[i];
presolve();
cout << solve(1, n) << endl;
}
return 0;
}