[贪心] P7599 [APIO2021] 雨林跳跃
posted on 2025-05-04 02:20:20 | under | source
题意:给出高度数组 \(h_1\dots h_n\) 为一个排列,每次在 \(x\) 时可以向左或向右跳到第一个 \(h_y>h_x\) 的 \(y\) 上。\(q\) 次询问,每次给出 \(A,B,C,D\),求起点在 \([A,B]\)、终点在 \([C,D]\) 的最少跳跃次数,或报告无解。\(n\le 2\times 10^5,q\le 10^5\)。
先从有无解下手,发现 \(\max[B,C)\) 是一定逃不掉的,所以假如 \(\max[B,C)>\max[C,D]\) 一定无解,反之只需从 \(B\) 开始一直向右跳就行了。所以充要条件就是 \(\max[B,C)<\max[C,D]\)。
考虑固定起终点 \(s,t\) 怎么最小化步数(设有解)。考虑向左向右的两个决策,发现在两个高度都 \(\le h_t\) 时跳向较高者更优。反证,在第一次高度越过较高者时直接调整即可,如下图。当高度越过 \(h_t\) 这个阈值时,发现越界者一定是左边的(不然无解),一直往右跳即可。

先来处理起点,贪心地想应当选较高者出发,前提是合法。结合前面的判定方法,合法区间是 \([A,B]\) 一段后缀,可以二分找到。证明的话也是反证,和前面差不多。
最后是终点,只需将之前求最小值那个做法改一下,把阈值设为 \(\max[B,C)\),因为它必然被越过。然后直接向右跳,肯定是优的。
注意一下细节,就是处理完在阈值内倍增跳后,此时假如可以跳到的较高者低于 \(\max[C,D]\),肯定跳更优。但是注意假如我已经在 \(\max[B,C)\) 上了就不用跳了。
复杂度 \(O(q\log n)\)。
代码
#include<bits/stdc++.h>
using namespace std;
#define chkmx(a, b) (h[a] > h[b] ? (a) : (b))
#define pir pair<int, int>
const int NN = 2e5 + 5;
int n, T, h[NN], mx, stk[NN], stt, ls[NN], rs[NN], fa[NN][20], rr[NN][20], st[NN][20], lg[NN];
int A, B, C, D;
vector<int> sn[NN];
inline void INIT(){
for(int i = 2; i < NN; ++i) lg[i] = lg[i >> 1] + 1;
stt = 0;
for(int i = 1; i <= n; ++i){
while(stt && h[stk[stt]] < h[i]) --stt;
ls[i] = stk[stt], stk[++stt] = i;
}
stt = 0;
for(int i = n; i; --i){
while(stt && h[stk[stt]] < h[i]) --stt;
rs[i] = rr[i][0] = stk[stt], stk[++stt] = i;
for(int j = 1; j < 20; ++j) rr[i][j] = rr[rr[i][j - 1]][j - 1];
}
for(int j = 0; j < 20; ++j)
for(int i = 1; i + (1 << j) - 1 <= n; ++i){
if(!j) st[i][j] = i;
else st[i][j] = chkmx(st[i][j - 1], st[i + (1 << j - 1)][j - 1]);
}
}
inline int getmx(int l, int r){
int len = lg[r - l + 1];
return chkmx(st[l][len], st[r - (1 << len) + 1][len]);
}
inline void dfs(int u){
for(int i = 1; i < 20; ++i) fa[u][i] = fa[fa[u][i - 1]][i - 1];
for(auto v : sn[u]) fa[v][0] = u, dfs(v);
}
inline int jp(int &x, int lim){
int cnt = 0;
for(int i = 19; ~i; --i)
if(fa[x][i] && h[fa[x][i]] <= lim) cnt += (1 << i), x = fa[x][i];
return cnt;
}
inline int jp2(int &x, int lim){
int cnt = 0;
for(int i = 19; ~i; --i)
if(rr[x][i] && rr[x][i] < lim) cnt += (1 << i), x = rr[x][i];
return cnt;
}
int minimum_jumps(int A, int B, int C, int D) {
++A, ++B, ++C, ++D;
int ans = 0;
if(h[getmx(B, C - 1)] > h[getmx(C, D)]) return -1;
int L = A - 1, R = B, mid, x;
while(L + 1 < R){
mid = (L + R) >> 1;
if(h[getmx(mid, C - 1)] < h[getmx(C, D)]) R = mid;
else L = mid;
}
x = getmx(R, B);
int mxx = getmx(x, C - 1);
ans += jp(x, h[mxx] - 1);
if(x != mxx && h[fa[x][0]] < h[getmx(C, D)]) x = fa[x][0], ++ans;
ans += jp2(x, C) + 1;
return ans;
}
void init(int N, std::vector<int> H) {
n = N;
for(int i = 1; i <= n; ++i) h[i] = H[i - 1], chkmx(mx, i);
INIT();
for(int i = 1; i <= n; ++i) sn[chkmx(ls[i], rs[i])].push_back(i);
dfs(mx);
}

浙公网安备 33010602011771号