1 条题解

  • 0
    @ 2026-5-12 17:57:57

    题解:D. Kousuke 的作业

    问题重述

    给定一个长度为 nn 的数组 aa,我们需要找到最多不重叠的子段,使得每个子段的元素和为 00


    第一步:转化为前缀和问题

    定义前缀和数组 pp

    p0=0,pi=j=1iajp_0 = 0,\quad p_i = \sum_{j=1}^{i} a_j

    那么对于子段 [l,r][l, r],其元素和为:

    al+al+1++ar=prpl1a_l + a_{l+1} + \dots + a_r = p_r - p_{l-1}

    子段和为 00 的条件等价于:

    pr=pl1p_r = p_{l-1}

    也就是说,一个子段是美丽的,当且仅当其左右端点对应的前缀和相等


    第二步:经典 DP 模型

    这是一个典型的“最大不重叠区间”问题。

    dp[i]dp[i] 表示考虑前 ii 个元素时,最多能选出的不重叠美丽子段数。

    假设我们当前有一个美丽子段 [l,r][l, r],那么:

    dp[r]=max(dp[r1],  dp[l1]+1)dp[r] = \max(dp[r-1],\; dp[l-1] + 1)

    其中:

    • dp[r1]dp[r-1] 表示不选当前子段;
    • dp[l1]+1dp[l-1] + 1 表示选当前子段,并加上前面的最优解。

    我们按子段的右端点 rr 从小到大处理。
    由于我们处理到 rr 时,所有 i<ri < rdp[i]dp[i] 都已经计算完毕,因此该 DP 是可行的。

    最终答案为 dp[n]dp[n]


    第三步:针对本题的优化

    如果对于某个右端点 rr,存在多个 ll 使得 pl1=prp_{l-1} = p_r,我们应该选哪一个?

    我们只需要选 最大的 ll(即最靠右的左端点)对应的子段。
    为什么?

    假设有两个左端点 l1<l2l_1 < l_2 都满足 pl11=pl21=prp_{l_1-1} = p_{l_2-1} = p_r
    那么子段 [l2,r][l_2, r][l1,r][l_1, r] 的子段。
    如果选了较长的 [l1,r][l_1, r],那么内部必然包含更短的 [l2,r][l_2, r],这会导致无法再选取内部的子段,从而可能错过最优解。
    而选择最短的子段 [l2,r][l_2, r] 不会浪费内部可能存在的其他美丽子段。

    因此,对于每个 rr,若存在 ll 满足条件,我们只需要考虑最小的子段(即 ll 最大的那个)。


    第四步:算法实现

    1. 计算前缀和 pip_ii=0,1,,ni = 0, 1, \dots, n
    2. 使用哈希表(如 unordered_map)记录每个前缀和值最后一次出现的位置
    3. 遍历 i=1i = 1nn
      • pip_i 之前出现过,设其最后一次出现的位置为 lastlast(注意:这里 lastlast 对应的是前缀和的下标,即子段的左端点为 last+1last+1,右端点为 ii)。
      • 对于这个子段 [last+1,i][last+1, i],执行 DP:dp[i]=max(dp[i1],  dp[last]+1)dp[i] = \max(dp[i-1],\; dp[last] + 1)
      • 否则:dp[i]=dp[i1]dp[i] = dp[i-1]
      • 更新 pip_i 的最新出现位置为 ii
    4. 输出 dp[n]dp[n]

    第五步:时间复杂度

    • 每个前缀和值用哈希表存储,单次操作 O(1)O(1)
    • 遍历一次数组,总复杂度 O(n)O(n)
    • 排序或哈希表均可做到 O(n)O(n)O(nlogn)O(n \log n),这里实现为 O(n)O(n)

    示例验证

    示例 1

    数组:[2,1,3,2,1][2, 1, -3, 2, 1]
    前缀和:p=[0,2,3,0,2,3]p = [0, 2, 3, 0, 2, 3]

    • i=3i=3 时,p3=0p_3=0 上次出现在 00,子段 [1,3][1,3] 和为 00dp[3]=max(dp[2],dp[0]+1)=1dp[3] = \max(dp[2], dp[0]+1) = 1
    • i=4i=4 时,p4=2p_4=2 上次出现在 11,子段 [2,4][2,4] 和为 00dp[4]=max(dp[3],dp[1]+1)=1dp[4] = \max(dp[3], dp[1]+1) = 1
    • i=5i=5 时,p5=3p_5=3 上次出现在 22,子段 [3,5][3,5] 和为 00,但 dp[2]=1dp[2]=1dp[2]+1=2dp[2]+1=2dp[4]=1dp[4]=1dp[5]=1dp[5] = 1,因为最优是只选 [1,3][1,3][3,5][3,5] 中的一个。
      最终输出 11,与题意一致。

    总结

    本题的核心是:

    • 转化为前缀和相等问题;
    • 使用贪心选择最短子段;
    • 用 DP 求最大不重叠区间数。

    最终复杂度 O(n)O(n)

    • 1

    信息

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