Escape Through the Leaf(迫真)

· · 个人记录

但是这两个 Subtask 的做法都强依赖于固定了一边的端点(

所以虽然貌似有 81 pts 了但是好像跟正解没什么关系(

草(,关系很大。

起点一定是 \max([A,B])(这里 A 是修正后的左端点,即极小的满足 h_A \lt \max([C,D])A)(其实这是十分显然的,不知道为什么有人看不出来)。

所以倒过来使用 Sub6 的做法就做完了。于是你有了一个时间 O(q \log n) 空间 O(n \log n) 的 neuze 做法,感觉不是很好写 好写阿,很好写阿。

事实上你 Sub6 的做法也可以倍增 /yun

这里就考虑从 \max([A,B]) 正着跳到 [C,D] 里,考虑如果 rig_x \in [C,D] 那么 h_{rig_x} 满足什么条件,其实就是 h_{rig_x} \in (\max_{i=B}^{C-1} h_i , \max_{i=C}^{D} h_i](因为一定有 \text{max of } [B,C-1] \le \text{max of } [C,D]),那么因为跳的过程中 h_{rig} 是递增的,我们先倍增地跳到这个区间里,然后判断是否合法,是就直接跳进去求出答案;否则再继续跳到 h_{lef} \gt \max([C,D]) 的那个祖先就行了。因为有 h 的单调性,你甚至不需要建出上文所说的森林(

找性质应该找全(

注意边界(nxt / rig[0 ~ 17][n+1]),WA 了一万发(

constexpr int Q = 200003, oo = 2e9;
int n, Log2[Q], a[Q], seq[Q];
__attribute__((constructor)) static void
___________(){ for(int i = 2; i < Q; ++i) Log2[i] = Log2[i>>1] + 1; }

template<int N> struct MaxList {
    static const int LOG = 31-__builtin_clz(N); int st[LOG+1][N];
    inline int MAX(int i, int j){ return a[i] > a[j] ? i : j; }
    inline void Make(int n, int *a){ copy(a+1, a+n+1, st[0]+1); for(int j = 1; j <= Log2[n]; ++j) for(int i = n-(1<<j-1); i >= 1; --i) st[j][i] = MAX(st[j-1][i], st[j-1][i+(1<<j-1)]); }
    inline int Max(int l, int r){ int K = Log2[r-l+1]; return MAX(st[K][l], st[K][r-(1<<K)+1]); }
}; MaxList<Q> st; int L[Q], R[Q], nxt[18][Q], rig[18][Q], sta[Q], top;

void init(int N, vector<int> H) {
    n = N; for(int i = 1; i <= n; ++i) seq[i] = i;
    copy(begin(H), end(H), a+1), st.Make(n, seq);
    sta[top = 1] = 0, a[0] = +oo;
    for(int i = 1; i <= n; sta[++top] = i, ++i)
        for(L[i] = sta[top]; a[sta[top]] <= a[i]; L[i] = sta[--top]);
    sta[top = 1] = n+1, a[n+1] = +oo;
    for(int i = n; i >= 1; sta[++top] = i, --i)
        for(R[i] = sta[top]; a[sta[top]] <= a[i]; R[i] = sta[--top]);
    for(int i = 1; i <= n; ++i)
        rig[0][i] = R[i], nxt[0][i] = a[L[i]] > a[R[i]] ? L[i] : R[i];
    rig[0][n+1] = nxt[0][n+1] = n+1;
    for(int j = 1; j < 18; ++j)
        for(int i = 1; i <= n+1; ++i)
            nxt[j][i] = nxt[j-1][nxt[j-1][i]], rig[j][i] = rig[j-1][rig[j-1][i]]; 
//  fprintf(stderr, "!!!! ");
//  for(int i = n; i >= 1; --i) fprintf(stderr, "(%d , %d)%c", R[i], dep[i], " \n"[i == 1]);
}
int minimum_jumps(int A, int B, int C, int D) {
//  fprintf(stderr, "sol [%d , %d] , [%d , %d]\n", A, B, C, D);
    ++A, ++B, ++C, ++D;
    int res = 0, ptr = 0, pos = st.Max(C, D);
    int Upd1 = a[st.Max(B, C-1)], Upd2 = a[pos];
    int l = A, r = B, mid, ans = B+1;
    while(l <= r) mid = l+r>>1, a[st.Max(mid, B)] < Upd2 ? (ans = mid, r = mid-1) : (l = mid+1);
    if(ans > B || Upd1 > Upd2) return -1; ptr = st.Max(ans, B);
    for(int j = 17; j >= 0; --j)
        if(a[nxt[j][ptr]] <= Upd1) res += 1<<j, ptr = nxt[j][ptr];
    if(ptr >= C && ptr <= D) return res; // 已经在区间内
    if(R[ptr] >= C && R[ptr] <= D) return res + 1; // 跳一步
    if(R[nxt[0][ptr]] >= C && R[nxt[0][ptr]] <= D) return res + 2; // 跳两步
    for(int j = 17; j >= 0; --j)
        if(rig[j][ptr] < C) res += 1<<j, ptr = rig[j][ptr]; // 跳祖先
    return res + 1;
}