1 条题解

  • 0
    @ 2025-10-27 18:08:54

    问题重述

    给定一个 11NN 的排列 pp,和一个长度为 N1N-1UD 组成的字符串 ss

    要求找到最长的子序列 a0,a1,,aKa_0, a_1, \dots, a_K,使得对于每个 j=1,,Kj = 1, \dots, K

    • 如果 sj=’U’s_j = \text{'U'},则 aj1<aja_{j-1} < a_j
    • 如果 sj=’D’s_j = \text{'D'},则 aj1>aja_{j-1} > a_j

    输出最大的 KK


    关键观察

    1. 问题本质

    这是带模式的最长交替子序列问题。模式由字符串 ss 指定,决定了相邻元素之间必须是上升还是下降关系。

    2. 暴力 DP

    最直接的想法是定义: dp[i][k]dp[i][k] = 考虑前 ii 个元素,匹配了模式的前 kk 个字符,且以 pip_i 结尾时的最小/最大值(取决于当前模式)

    但状态数 O(N2)O(N^2) 对于 N3×105N \le 3\times 10^5 不可行。


    核心解法

    1. 状态重新设计

    关键洞察:我们只关心对于每个模式位置 kk,能够达到该位置的所有子序列的最后一个元素的值

    定义两个数组:

    • up[k]up[k] = 匹配了模式的前 kk 个字符,且最后一个字符满足当前模式要求(即如果 sk=’U’s_k = \text{'U'} 则要求上升)时,最后一个元素的最小值
    • down[k]down[k] = 匹配了模式的前 kk 个字符,且最后一个字符满足当前模式要求(即如果 sk=’D’s_k = \text{'D'} 则要求下降)时,最后一个元素的最大值

    这样设计的目的是:

    • 对于上升转移,我们希望最后一个元素尽可能小,为后续的上升留出空间
    • 对于下降转移,我们希望最后一个元素尽可能大,为后续的下降留出空间

    2. 转移过程

    我们按顺序处理排列中的每个元素 x=pix = p_i

    对于每个模式位置 kk

    情况 1:如果 sk+1=’U’s_{k+1} = \text{'U'}(下一个需要上升)

    • 可以从 up[k]up[k] 转移:如果 x>up[k]x > up[k],则 up[k+1]=min(up[k+1],x)up[k+1] = \min(up[k+1], x)
    • 可以从 down[k]down[k] 转移:如果 x>down[k]x > down[k],则 up[k+1]=min(up[k+1],x)up[k+1] = \min(up[k+1], x)

    情况 2:如果 sk+1=’D’s_{k+1} = \text{'D'}(下一个需要下降)

    • 可以从 up[k]up[k] 转移:如果 x<up[k]x < up[k],则 down[k+1]=max(down[k+1],x)down[k+1] = \max(down[k+1], x)
    • 可以从 down[k]down[k] 转移:如果 x<down[k]x < down[k],则 down[k+1]=max(down[k+1],x)down[k+1] = \max(down[k+1], x)

    3. 初始化

    • up[0]=0up[0] = 0(实际上用一个极小值,表示还没有任何元素)
    • down[0]=down[0] = \infty(用一个极大值)

    当我们处理第一个元素 xx 时:

    • 可以直接开始一个长度为 1 的子序列
    • 根据 s1s_1U 还是 D 来初始化 up[1]up[1]down[1]down[1]

    算法优化

    1. 单调性观察

    up[k]up[k] 随着 kk 增加是非递减的(因为每次上升转移都会选择尽可能小的值) down[k]down[k] 随着 kk 增加是非递增的(因为每次下降转移都会选择尽可能大的值)

    2. 二分优化

    利用单调性,我们可以用二分查找来快速找到能够进行转移的模式位置:

    对于当前元素 xx

    • 如果下一个模式字符是 U,找到最大的 kk 使得 up[k]<xup[k] < xdown[k]<xdown[k] < x
    • 如果下一个模式字符是 D,找到最大的 kk 使得 up[k]>xup[k] > xdown[k]>xdown[k] > x

    这样可以将复杂度从 O(NK)O(NK) 优化到 O(NlogK)O(N \log K),其中 KK 是最终答案。

    3. 实际实现

    维护两个数组 upupdowndown,初始时:

    • up[0]=0up[0] = 0, down[0]=down[0] = \infty
    • 其他位置 up[k]=up[k] = \infty, down[k]=0down[k] = 0

    对于每个元素 xx

    1. upup 数组中二分找到最大的 k1k_1 满足 up[k1]<xup[k_1] < x
    2. downdown 数组中二分找到最大的 k2k_2 满足 down[k2]<xdown[k_2] < x
    3. k=max(k1,k2)k = \max(k_1, k_2)
    4. 如果 sk+1=’U’s_{k+1} = \text{'U'},则 up[k+1]=min(up[k+1],x)up[k+1] = \min(up[k+1], x)
    5. 如果 sk+1=’D’s_{k+1} = \text{'D'},则 down[k+1]=min(down[k+1],x)down[k+1] = \min(down[k+1], x)(这里需要对称处理下降情况)

    复杂度分析

    • 对于每个元素:两次二分查找 O(logK)O(\log K)
    • 总复杂度:O(NlogK)O(N \log K),其中 KNK \le N
    • 由于 N3×105N \le 3\times 10^5,完全可行

    总结

    这道题的核心在于:

    1. 状态设计:用 up[k]up[k]down[k]down[k] 分别记录两种模式下的最优结尾值
    2. 贪心思想:上升时选择尽可能小的结尾,下降时选择尽可能大的结尾
    3. 单调性利用upup 数组单调不减,downdown 数组单调不增
    4. 二分优化:利用单调性快速找到可转移的位置

    通过将问题转化为维护两个具有单调性的数组,并利用二分查找进行高效转移,我们成功地将 O(N2)O(N^2) 的 DP 优化到了 O(NlogN)O(N \log N),解决了这个看似复杂的问题。

    • 1

    「USACO 2022 US Open Platinum」Up Down Subsequence

    信息

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