제출 #1258757

#제출 시각아이디문제언어결과실행 시간메모리
1258757christhegamechangerObstacles for a Llama (IOI25_obstacles)C++17
37 / 100
353 ms14820 KiB
#include <bits/stdc++.h>
using namespace std;

namespace Obstacles {

// ---------- Segment tree trên H: min + tìm rào H>=t ----------
struct SegTree {
    int n;
    vector<int> mn, mx; // mn: min(H), mx: max(H) để prune các truy vấn GE

    void build(int id, int l, int r, const vector<int>& a) {
        if (l == r) { mn[id] = mx[id] = a[l]; return; }
        int m = (l + r) >> 1;
        build(id<<1, l, m, a);
        build(id<<1|1, m+1, r, a);
        mn[id] = min(mn[id<<1], mn[id<<1|1]);
        mx[id] = max(mx[id<<1], mx[id<<1|1]);
    }
    void init(const vector<int>& a) {
        n = (int)a.size();
        mn.assign(4*n, INT_MAX);
        mx.assign(4*n, INT_MIN);
        if (n) build(1, 0, n-1, a);
    }
    int qmin(int id, int l, int r, int ql, int qr) const {
        if (ql > qr || qr < l || r < ql) return INT_MAX;
        if (ql <= l && r <= qr) return mn[id];
        int m = (l + r) >> 1;
        return min(qmin(id<<1, l, m, ql, qr), qmin(id<<1|1, m+1, r, ql, qr));
    }
    int qmin(int l, int r) const {
        if (l > r) return INT_MAX;              // guard đoạn rỗng
        return qmin(1, 0, n-1, l, r);
    }
    // first index in [ql,qr] with H[idx] >= t, or -1
    int first_ge(int id, int l, int r, int ql, int qr, int t) const {
        if (ql > qr || qr < l || r < ql || mx[id] < t) return -1;
        if (l == r) return l;
        int m = (l + r) >> 1;
        int left = first_ge(id<<1, l, m, ql, qr, t);
        return (left != -1) ? left : first_ge(id<<1|1, m+1, r, ql, qr, t);
    }
    int first_ge(int l, int r, int t) const {
        if (l > r) return -1;
        return first_ge(1, 0, n-1, l, r, t);
    }
    // last index in [ql,qr] with H[idx] >= t, or -1
    int last_ge(int id, int l, int r, int ql, int qr, int t) const {
        if (ql > qr || qr < l || r < ql || mx[id] < t) return -1;
        if (l == r) return l;
        int m = (l + r) >> 1;
        int right = last_ge(id<<1|1, m+1, r, ql, qr, t);
        return (right != -1) ? right : last_ge(id<<1, l, m, ql, qr, t);
    }
    int last_ge(int l, int r, int t) const {
        if (l > r) return -1;
        return last_ge(1, 0, n-1, l, r, t);
    }
};

int N, M;
vector<int> Trow, Hcol;
vector<int> prefMinT, prefMaxT;
SegTree segH;

// sâu nhất r sao cho prefixMin(0..r) > h ; -1 nếu không có
inline int last_r_with_prefMin_gt(int h) {
    int lo = 0, hi = N - 1, ans = -1;
    while (lo <= hi) {
        int mid = (lo + hi) >> 1;
        if (prefMinT[mid] > h) { ans = mid; lo = mid + 1; }
        else hi = mid - 1;
    }
    return ans;
}

// dải cột quanh X trong [L,R] với điều kiện H < t
inline pair<int,int> span_around(int L, int R, int X, int t) {
    // rào trái/phải giới hạn trong [L..X] và [X..R]
    int left_bar  = segH.last_ge(L, X, t);
    int right_bar = segH.first_ge(X, R, t);
    int Lb = max(L, left_bar + 1);
    int Rb = min(R, (right_bar == -1 ? R : right_bar - 1));
    return {Lb, Rb};
}

} // namespace Obstacles

using namespace Obstacles;

// -------- required API --------
void initialize(vector<int> T, vector<int> H) {
    Trow = move(T); Hcol = move(H);
    N = (int)Trow.size(); M = (int)Hcol.size();

    // prefix min/max của T
    prefMinT.assign(N, 0);
    prefMaxT.assign(N, 0);
    for (int i = 0; i < N; ++i) {
        if (i == 0) prefMinT[i] = prefMaxT[i] = Trow[i];
        else {
            prefMinT[i] = min(prefMinT[i-1], Trow[i]);
            prefMaxT[i] = max(prefMaxT[i-1], Trow[i]);
        }
    }

    // segment tree trên H
    segH.init(Hcol);
}

bool can_reach(int L, int R, int S, int D) {
    // Guard theo mô tả đề: (0,S) và (0,D) đều hợp lệ, nhưng ta vẫn chặn an toàn.
    if (!(Trow[0] > Hcol[S] && Trow[0] > Hcol[D])) return false;

    // Dải hàng-0 quanh D (H < T[0])
    int t0 = Trow[0];
    auto dseg = span_around(L, R, D, t0);
    int DL = dseg.first, DR = dseg.second;      // luôn DL ≤ D ≤ DR

    // Khởi tạo dải quanh S với tcur = T[0]
    int tcur = t0;
    auto sseg = span_around(L, R, S, tcur);
    int Lb = sseg.first, Rb = sseg.second;      // luôn Lb ≤ S ≤ Rb

    // Lặp mở rộng theo "thang máy" tốt nhất; số vòng ≤ 11 (miền giá trị 0..10)
    for (int iter = 0; iter < 20; ++iter) {     // 20 = guard thừa an toàn
        // Độ ẩm nhỏ nhất trong dải hiện tại
        int hmin = segH.qmin(Lb, Rb);           // ∈ [0..10]
        int rmax = last_r_with_prefMin_gt(hmin);
        if (rmax < 0) return false;             // không thể đi xuống (guard)

        // Ngưỡng ngang tốt nhất sau khi đi xuống ≤ rmax
        int tnew = prefMaxT[rmax];

        // Dải mới quanh S theo tnew
        auto sseg2 = span_around(L, R, S, tnew);
        int L2 = sseg2.first, R2 = sseg2.second;

        // Kiểm tra khả năng kết thúc tại (0,D):
        // cần giao giữa dải mới của S và dải hàng-0 của D,
        // và có cột trên giao đủ "khô" để leo thẳng về hàng 0.
        int IL = max(L2, DL), IR = min(R2, DR);
        if (IL <= IR) {
            int h_inter = segH.qmin(IL, IR);    // INT_MAX nếu rỗng (nhưng đã IL<=IR)
            if (h_inter < prefMinT[rmax]) return true;
        }

        // Không thể nới thêm → bế tắc
        if (tnew <= tcur) return false;

        // Cập nhật cho vòng tiếp
        tcur = tnew;
        Lb = L2; Rb = R2;
    }
    return false; // không tới được (guard)
}
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...