1 条题解

  • 0
    @ 2025-10-28 11:03:37

    问题重述

    我们有一棵 nn 个节点的二叉树,每个节点 ii 初始有 did_i 道题目。需要按某种顺序断开所有边,每次断开边 (u,v)(u,v) 时,uuvv 会交换它们当前的所有题目,交换的题目数量等于两者当前题目数之和。目标是 minimize 总交换题目数。


    关键观察

    1. 交换的累积效应

    设节点 uu 在断开与父节点的边之前,其子树内的总交换次数为 TuT_u,子树内的总题目数为 SuS_u

    当断开 uu 与父节点的边时:

    • 交换的题目数 = uu 的当前题目数 + 父节点的当前题目数
    • uu 的当前题目数 = dud_u + 从子节点交换来的题目

    2. 子树贡献分析

    对于节点 uu,假设它有左右子节点 llrr

    断开边 (u,l)(u,l)(u,r)(u,r) 的顺序会影响总代价:

    • 先断左后断右

      • (u,l)(u,l):代价 = (du+从右子树来的题目?)+(dl+从左子树的子树来的题目?)(d_u + 从右子树来的题目?) + (d_l + 从左子树的子树来的题目?)
      • (u,r)(u,r):代价 = (du+dl+从左右子树来的题目?)+(dr+从右子树的子树来的题目?)(d_u + d_l + 从左右子树来的题目?) + (d_r + 从右子树的子树来的题目?)
    • 先断右后断左:对称情况

    实际上,经过推导可以发现:对于节点 uu,断开左右子树的顺序不影响该节点贡献的总代价


    核心解法

    1. 树形 DP 状态设计

    设:

    • size[u]size[u] = 以 uu 为根的子树中的初始题目总数
    • dp[u]dp[u] = 断开以 uu 为根的子树中所有边的最小总代价

    2. 状态转移

    对于节点 uu

    • 如果没有子节点:dp[u]=0dp[u] = 0size[u]=dusize[u] = d_u

    • 如果只有一个子节点 vv

      dp[u] = dp[v] + size[v] + d_u
      size[u] = size[v] + d_u
      

      解释:断开 (u,v)(u,v) 时,uudud_u 题,vvsize[v]size[v] 题(因为 vv 的子树已断开),代价为 du+size[v]d_u + size[v],再加上 vv 子树的代价 dp[v]dp[v]

    • 如果有两个子节点 llrr

      dp[u] = dp[l] + dp[r] + size[l] + size[r] + d_u
      size[u] = size[l] + size[r] + d_u
      

      解释:无论先断左还是先断右,总代价都是 dp[l]+dp[r]+(断开(u,l)的代价)+(断开(u,r)的代价)dp[l] + dp[r] + (断开(u,l)的代价) + (断开(u,r)的代价) 而断开 (u,l)(u,l) 时,uudu+size[r]d_u + size[r] 题,llsize[l]size[l] 题,代价为 du+size[r]+size[l]d_u + size[r] + size[l] 断开 (u,r)(u,r) 时,uudu+size[l]d_u + size[l] 题,rrsize[r]size[r] 题,代价为 du+size[l]+size[r]d_u + size[l] + size[r] 两者相同,所以顺序不影响。

    3. 最终答案

    整棵树断开的总代价就是 dp[root]dp[root],其中 root=1root = 1


    算法步骤

    1. 建树:根据输入的 cic_i 构建二叉树
    2. DFS 后序遍历:计算每个节点的 dp[u]dp[u]size[u]size[u]
    3. 输出dp[1]dp[1]

    样例验证

    以样例为例:

    n = 3
    d = [2, 1, 3]  
    c = [1, 1]  # 表示节点2和节点3的父节点都是1
    

    树结构:节点1有两个子节点2和3

    计算过程:

    • 节点2:dp[2]=0dp[2] = 0, size[2]=1size[2] = 1
    • 节点3:dp[3]=0dp[3] = 0, size[3]=3size[3] = 3
    • 节点1:$dp[1] = dp[2] + dp[3] + size[2] + size[3] + d_1 = 0 + 0 + 1 + 3 + 2 = 6$

    但样例答案是7,说明我们的推导还需要修正。


    修正分析

    重新分析两个子节点的情况:

    情况1:先断左后断右

    • (u,l)(u,l)uudud_ullsize[l]size[l],代价 = du+size[l]d_u + size[l]
    • (u,r)(u,r)uudu+size[l]d_u + size[l]rrsize[r]size[r],代价 = du+size[l]+size[r]d_u + size[l] + size[r]
    • 总代价 = $dp[l] + dp[r] + (d_u + size[l]) + (d_u + size[l] + size[r])$

    情况2:先断右后断左

    • (u,r)(u,r)uudud_urrsize[r]size[r],代价 = du+size[r]d_u + size[r]
    • (u,l)(u,l)uudu+size[r]d_u + size[r]llsize[l]size[l],代价 = du+size[r]+size[l]d_u + size[r] + size[l]
    • 总代价 = $dp[l] + dp[r] + (d_u + size[r]) + (d_u + size[r] + size[l])$

    选择最优顺序

    • 如果 size[l]<size[r]size[l] < size[r],先断左再断右更优
    • 如果 size[l]>size[r]size[l] > size[r],先断右再断左更优

    因此正确的转移方程:

    dp[u] = dp[l] + dp[r] + min(
        size[l] + (2*d_u + size[l] + size[r]),  # 先左后右
        size[r] + (2*d_u + size[l] + size[r])   # 先右后左
    )
    = dp[l] + dp[r] + 2*d_u + size[l] + size[r] + min(size[l], size[r])
    

    最终算法

    对于节点 uu

    • 叶子节点:dp[u]=0dp[u] = 0, size[u]=dusize[u] = d_u
    • 一个子节点 vvdp[u]=dp[v]+size[v]+dudp[u] = dp[v] + size[v] + d_u, size[u]=size[v]+dusize[u] = size[v] + d_u
    • 两个子节点 l,rl, r:$dp[u] = dp[l] + dp[r] + 2*d_u + size[l] + size[r] + min(size[l], size[r])$, size[u]=size[l]+size[r]+dusize[u] = size[l] + size[r] + d_u

    复杂度分析

    • 时间复杂度O(n)O(n),每个节点处理一次
    • 空间复杂度O(n)O(n),存储树结构和DP状态

    总结

    这道题的核心在于:

    1. 理解交换的累积效应:节点题目数随交换过程变化
    2. 分析断开顺序的影响:对于二叉树节点,需要选择先断开哪个子树的边
    3. 贪心策略:优先断开子树总题目数较小的边
    4. 树形DP:自底向上计算最小代价

    通过仔细分析交换过程和断开顺序的影响,我们得到了一个简洁高效的线性算法。

    • 1

    「联合省选 2022」最大权独立集问题

    信息

    ID
    4438
    时间
    1000ms
    内存
    1024MiB
    难度
    7
    标签
    递交数
    5
    已通过
    1
    上传者