【luogu P7599】雨林跳跃

雨林跳跃

题目大意:luogu P7599

题目大意

有一排数,它们互不相同。
我们将移动定义为向左或向右走到第一个大于它的数。
现在多组询问,每次给出起点范围和终点范围,要你选起点终点,然后能走到而且用的步数最少。
如果无论如何都走不到终点就输出 -1。

思路

小性质

首先我们要发现一些性质:
是不会有跳到终点区间右边再跳回来的情况。因为终点在起点右边,那你要跳到终点右边,你跳到的位置一定会大于你起点到当前点的值,那你这个值已经比答案区间的所有值都高了,就跳不回去了。

那你就想到有往左走和往高走两个选项。
一个是为了走到终点,一个是为了更快的走到要的高度。

终点

我们设 MBC 区间最高的,NCD 区间最高的。
在这里插入图片描述
那最后一步一定是这样的:
在这里插入图片描述

从一个高于等于 M 小于 N 的地方向右跳,而且要 EEM 区间最高的。
那这样跳就会跳入 CN 区间,就在答案区间中,我们把这些 E 叫做准终点。
那其实问题可以是如何最快走到准终点,那终点也确定了,就根据高度的限定。

确定起点

那接着我们开始想一些问题,起点选哪个?
先说说结果,如果有准终点就它,否则选 AB 中最高的 S 使得 SM 中除了 M S 最高,当然它要小于 N
为什么呢?有准终点就很显然,没有的我们画图看看:
在这里插入图片描述
P 是选的,R,Q 分别表示它左跳右跳跳到的位置)
Q 一定不在 AB 段,否则它才是 P
由于 AB 间没有准节点,所以 R 会比 N 高。(如果不比它搞那它就应该是 P)所以 AR 间都不能做起点。而如果 R 跑到 A 左边,虽然不一定比 N 高,但它就属于后面证好的 RP 中,不需要证了。
RP 之间由于 P 跳到 R 有都低于 P,所以要跳出去一定会碰到 R,P 其中一个,所以不优。
PQ 之间同样的道理,但由于 Q 不在 AB 中,那 P 必然一次到 Q,故别的不会比 P 更优。
那就从 P 最优。

那接着说说怎么找到,那容易看到就是从 B 开始不停往左跳,如果跳到的下一个在 A 左边或大于 N 就不跳停下。

跳的策略

那接着就是要怎么跳了。

那如果两边都能跳,而且跳了都不会大于等于 M,那肯定选高的跳,加快速度。
那如果跳高的发现大于等于 M,那就判断一下这样是不是在准终点,如果是就走。
否则不走,然后一直往右跳,直到跳到准终点。(当然如果跳不到就是无解)

怎么跳

容易想到倍增,就倍增左跳,右跳,高跳。
然后一开始求 M,N 是求区间最大值,可以用 ST 表来搞。(当然也要倍增)

代码

#include <vector> #include<cstdio> #include<iostream> #define INF 0x3f3f3f3f3f3f3f3f int n, h[200001], high[200001][21], left[200001][21], righ[200001][21]; int l[200001], r[200001], sta[200001], log2[200001]; int maxn[200001][21]; void init(int N, std::vector<int> H) { n = N; h[0] = INF; for (int i = 0; i < N; i++) h[i + 1] = H[i]; for (int i = 1; i <= n; i++) {//单调队列求左跳右跳跳到的位置 while (sta[0] && h[sta[sta[0]]] < h[i]) sta[0]--; l[i] = sta[sta[0]]; sta[++sta[0]] = i; } sta[0] = 0; for (int i = n; i >= 1; i--) { while (sta[0] && h[sta[sta[0]]] < h[i]) sta[0]--; r[i] = sta[sta[0]]; sta[++sta[0]] = i; } for (int i = 1; i <= n; i++) {//倍增的第一层 left[i][0] = l[i]; righ[i][0] = r[i]; if (l[i] && r[i]) high[i][0] = (h[l[i]] > h[r[i]]) ? l[i] : r[i]; maxn[i][0] = i; } for (int i = 1; (1 << i) <= n; i++)//倍增 for (int j = 1; j <= n; j++) { left[j][i] = left[left[j][i - 1]][i - 1]; righ[j][i] = righ[righ[j][i - 1]][i - 1]; high[j][i] = high[high[j][i - 1]][i - 1]; if (j + (1 << i) - 1 <= n)//只有它要判,因为别的不是一个一个跳的 maxn[j][i] = (h[maxn[j][i - 1]] > h[maxn[j + (1 << (i - 1))][i - 1]]) ? maxn[j][i - 1] : maxn[j + (1 << (i - 1))][i - 1]; } log2[1] = 0;//预处理log(ST表要用) for (int i = 2; i <= n; i++) log2[i] = log2[i >> 1] + 1; } int get_maxn(int l, int r) {//ST表求区间最大值 int log = log2[r - l + 1]; return (h[maxn[l][log]] > h[maxn[r - (1 << log) + 1][log]]) ? maxn[l][log] : maxn[r - (1 << log) + 1][log]; } int minimum_jumps(int A, int B, int C, int D) { A++; B++; C++; D++; int Mx = (B + 1 <= C - 1) ? h[get_maxn(B + 1, C - 1)] : 0; int Nx = h[get_maxn(C, D)]; int now = B, ans = 0; for (int i = 20; i >= 0; i--)//确定起点 if (left[now][i] >= A && h[left[now][i]] < Nx) now = left[now][i]; if (Mx <= h[now] && h[now] <= Nx) return 1; for (int i = 20; i >= 0; i--)//先向高跳 if (h[high[now][i]] < Mx) { ans += (1 << i); now = high[now][i]; } if (Mx <= h[high[now][0]] && h[high[now][0]] <= Nx) return ans + 2; for (int i = 20; i >= 0; i--)//往左跳 if (h[righ[now][i]] < Mx) { ans += (1 << i); now = righ[now][i]; } if (Mx <= h[r[now]] && h[r[now]] <= Nx) return ans + 2; return -1;//跳不到 }

__EOF__

本文作者あおいSakura
本文链接https://www.cnblogs.com/Sakura-TJH/p/luogu_P7599.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   あおいSakura  阅读(59)  评论(0编辑  收藏  举报
编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示