CF1696D Permutation Graph

题意

给定一个 nn 个数的 排列 aa,需要我们构造一张图,两点 i,j(i<j)i, j(i < j) 相连当且仅当 aia_iaja_jai,ai+1,,ara_i, a_{i+1}, \cdots, a_r 中的最大值和最小值或最小值和最大值。问 11 号点和 nn 号点间的最短路,有边相连的边权为 11

赛时就过了 AB,结果第二天 CD 都 2020 分钟不到过了。

解法

考虑贪心。

我们设 ai=na_i = n,那么 aia_i 就是整个排列的最大值。如果 i1i \neq 1ini \neq n,那么从 11 号点到 nn 号店显然必须通过点 ii

题目转化成处理 (1,i)(1, i)(i,n)(i, n) 的最短路之和。而处理这两个显然是等价的,所以先考虑 (1,i)(1, i)

假设在 (1,i)(1, i) 中存在 j(1<j<i)j(1 < j < i) 满足 j=mink=1iaij = \min_{k=1}^i a_i。那么从 11ii 一定需要经过点 jj。那么转化成求 (1,j)(1, j)(j,i)(j, i) 的最短路。可以发现 aja_j 最小,aia_i 最大,所以 jjii 一定连边。处理 (1,j)(1, j) 即可。而处理 (i,n)(i, n) 同理。显然最大值最小值直接前缀后缀 O(n)O(n) 预处理即可。

注意到每次递归,最多只会产生一个分叉,所以总复杂度 O(n)O(\sum n),单次 O(n)O(n)

官方题解还给了一个数据结构的做法,是 O(nlogn)O(n \log n) 的,但我觉得这个贪心更好理解。

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <vector>
using namespace std;

const int N = 3e5 + 5;

int t, n, a[N], premax[N], premin[N], sufmax[N], sufmin[N];
int maxp[N], minp[N], smax[N], smin[N];

int ans = 0;

void getans(int x, int y, bool f) // dis(x, y), f为true:最大值,f为false,最小值
{
	if (x == 1)
	{
		if ((premax[y] == a[y] && premin[y] == a[x]) || (premax[y] == a[x] && premin[y] == a[y]))
		{
			ans++;
			return;
		}
	}
	if (y == n)
	{
		if ((sufmax[x] == a[x] && sufmin[x] == a[y]) || (sufmax[x] == a[y] && sufmin[x] == a[x]))
		{
			ans++;
			return;
		}
	}
	if (y == n && x == 1)
	{
		if (premax[y] == a[y])
		{
			getans(1, minp[y], 1);
			ans++;
		}
		else if (premax[y] == a[x])
		{
			getans(minp[y], y, 1);
			ans++;
		}
		else
		{
			getans(1, maxp[y], 0);
			getans(maxp[y], y, 0);
		}
	}
	else
	{
		if (f)
		{
			if (x == 1)
			{
				int place = maxp[y];
				ans++;
				getans(1, place, 0);
			}
			else if (y == n)
			{
				int place = smax[x];
				ans++;
				getans(place, y, 0);
			}
		}
		else
		{
			if (x == 1)
			{
				int place = minp[y];
				ans++;
				getans(1, place, 1);
			}
			else if (y == n)
			{
				int place = smin[x];
				ans++;
				getans(place, y, 1);
			}
		}
	}
}

int main()
{
	memset(premin, 0x3f, sizeof premin);
	memset(sufmin, 0x3f, sizeof sufmin);
	scanf("%d", &t);
	while (t--)
	{
		ans = 0;
		scanf("%d", &n);
		for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
		if (n == 1)
		{
			printf("0\n");
			continue;
		}
		for (int i = 1; i <= n; i++)
		{
			if (a[i] > premax[i - 1])
			{
				premax[i] = a[i];
				maxp[i] = i;
			}
			else
			{
				premax[i] = premax[i - 1];
				maxp[i] = maxp[i - 1];
			}
			if (a[i] < premin[i - 1])
			{
				premin[i] = a[i];
				minp[i] = i;
			}
			else
			{
				premin[i] = premin[i - 1];
				minp[i] = minp[i - 1];
			}
		}
		for (int i = n; i >= 1; i--)
		{
			if (a[i] > sufmax[i + 1])
			{
				sufmax[i] = a[i];
				smax[i] = i;
			}
			else
			{
				sufmax[i] = sufmax[i + 1];
				smax[i] = smax[i + 1];
			}
			if (a[i] < sufmin[i + 1])
			{
				sufmin[i] = a[i];
				smin[i] = i;
			}
			else
			{
				sufmin[i] = sufmin[i + 1];
				smin[i] = smin[i + 1];
			}
		}
		getans(1, n, 1);
		printf("%d\n", ans);
		for (int i = 1; i <= n + 1; i++)
		{
			premax[i] = sufmax[i] = 0;
			premin[i] = sufmin[i] = 2e9;
			maxp[i] = minp[i] = smax[i] = smin[i] = 0;
		}
	}
	return 0;
}
posted @   HappyBobb  阅读(5)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示