1 条题解

  • 0
    @ 2025-11-9 13:10:30

    #5436. 「OOI 2016 Day 2」蝙蝠侠归来 题解

    问题分析

    我们需要在区间 [li,ri][l_i, r_i] 中找到一对下标 (p,q)(p, q),满足:

    • lip<qril_i \leq p < q \leq r_i
    • hp<hqh_p < h_q
    • 最大化 qpq - p

    如果没有满足条件的对,输出 -1 -1

    关键观察

    1. 问题转化

    这是一个区间最大跨度对查询问题,但带有高度约束 hp<hqh_p < h_q

    2. 候选对的性质

    对于最优解 (p,q)(p, q)

    • hph_p 应该是 [li,q)[l_i, q)小于 hqh_q最左边的元素
    • hqh_q 应该是 (p,ri](p, r_i]大于 hph_p最右边的元素

    3. 分治思路

    考虑分治解决:对于区间 [L,R][L, R],最优解要么完全在左半区间 [L,mid][L, mid],要么完全在右半区间 [mid+1,R][mid+1, R],要么跨越中点。

    跨越中点的候选解

    • 对每个右半区的 qq,在左半区找到满足 hp<hqh_p < h_qpp 最小的位置
    • 对每个左半区的 pp,在右半区找到满足 hq>hph_q > h_pqq 最大的位置

    算法框架

    1. 分治预处理

    使用分治算法预处理所有可能的候选对:

    def solve(L, R):
        if L == R: return []
        mid = (L + R) // 2
        
        # 递归解决左右子问题
        left_pairs = solve(L, mid)
        right_pairs = solve(mid+1, R)
        
        # 处理跨越中点的候选对
        cross_pairs = []
        
        # 情况1:对每个右半区的q,找左半区最小的p满足h_p < h_q
        # 情况2:对每个左半区的p,找右半区最大的q满足h_q > h_p
        
        return merge(left_pairs, right_pairs, cross_pairs)
    

    2. 候选对存储

    对于每个区间 [L,R][L, R],我们存储:

    • 所有可能成为最优解的候选对 (p,q)(p, q)
    • 这些候选对按某种顺序组织以便快速查询

    3. 查询处理

    对于查询 [l,r][l, r]

    • 找到完全包含 [l,r][l, r] 的线段树节点
    • 在这些节点的候选对中寻找满足 lp<qrl \leq p < q \leq rqpq-p 最大的对

    优化技巧

    1. 单调性利用

    在分治的跨越中点步骤中,可以使用单调栈来高效找到候选对:

    • 从左往右扫描左半区,维护高度递减的单调栈
    • 从右往左扫描右半区,维护高度递增的单调栈

    2. 候选对剪枝

    对于候选对 (p,q)(p, q),如果存在 (p,q)(p', q') 满足 ppp' \geq pqqq' \leq qqpqpq'-p' \geq q-p,那么 (p,q)(p, q) 可以被剪枝。

    3. 线段树构建

    将整个数组建立线段树,每个节点存储该区间内的所有候选对(经过剪枝的)。

    复杂度分析

    • 预处理O(nlog2n)O(n \log^2 n),分治过程中每个区间需要 O(len)O(\text{len}) 时间处理
    • 存储空间O(nlogn)O(n \log n),每个元素出现在 O(logn)O(\log n) 个区间中
    • 单次查询O(log2n)O(\log^2 n),需要在 O(logn)O(\log n) 个节点中分别二分查找
    • 1

    信息

    ID
    4964
    时间
    1000ms
    内存
    256MiB
    难度
    10
    标签
    递交数
    1
    已通过
    1
    上传者