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 号结点的最短路

思路

  1. 假设 \(a_L=1,a_R=n\) (相反亦可),则从 1 号点到 n 号点一定要经过 L,R 两点,不能直接越过它们;L 号点到 R 号点可以一次通过,因此 \(solve(1,n)=solve(1,L)+1+solve(R,n)\)
  2. 继续递归下去,找到 \([1,L]\) 的最大值与最小值,继续递归。。。
  3. 可以用 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;
}
posted @ 2022-09-22 17:45  hzy0227  阅读(12)  评论(0编辑  收藏  举报