1 条题解

  • 0
    @ 2025-11-2 18:46:52

    题目概述

    公司组织结构是一棵树,根节点是总经理(编号1)。每个员工有一个能力值 svs_v,各不相同。每天总经理被解雇后,由能力最高的直接下属接任。

    员工可以“取消”一位同事(将其能力降为0),来加速自己成为总经理的进程。需要回答多个查询:对于员工 vkv_k,在恰好取消一位同事的情况下,最少需要多少天才能成为总经理。


    问题分析

    关键观察

    1. 晋升规则:每天总经理被能力最高的直接下属取代
    2. 树结构:公司是树形结构,父节点是直接上级
    3. 取消操作:可以让一个员工能力变为0,影响晋升路径
    4. 目标:最小化成为总经理所需天数

    问题难点

    • 需要动态计算取消不同员工的影响
    • 树规模很大(n300,000n \leq 300,000
    • 需要高效处理多个查询

    算法思路

    核心思想:重链剖分 + 线段树

    步骤1:建立能力树

    将原树按照能力值重新构建:

    • 节点编号变为能力值(svs_v
    • 边表示晋升关系:从高能力指向低能力

    步骤2:计算晋升顺序

    使用大根堆模拟晋升过程:

    • 初始将根节点(能力最高的总经理)入堆
    • 每次取出堆顶(当前能力最高的候选人)
    • 将其所有下属加入堆中
    • 这样得到员工成为总经理的自然顺序

    步骤3:重链剖分优化

    在能力树上进行重链剖分,用线段树维护:

    • 每个节点的“深度偏移量”
    • 快速查询路径上的最大值

    步骤4:计算取消影响

    对于每个员工 uu,计算如果取消某个员工,uu 能提前多少天成为总经理:

    • 在能力树上,uu 的祖先路径上的节点会阻碍 uu 晋升
    • 取消一个员工相当于移除一个障碍
    • 使用重链剖分快速计算最优取消方案

    算法详解

    数据结构

    线段树 (segtree)

    支持区间加和查询全局最大值:

    class segtree {
        vector<pii> B;  // 区间边界
        vector<ll> S, T; // 最大值、懒标记
        // 支持区间加和全局最大值查询
    };
    

    重链剖分 (hld)

    在能力树上进行剖分:

    class hld {
        vector<int> f, e, h, t, l; // 父节点、子树大小、重儿子、链顶、DFS序
        segtree T;  // 线段树
        
        // 更新节点u到根的路径
        void update(int u, int d) {
            while (~u) {
                T.update(1, l[t[u]], l[u], d); // 更新整条链
                u = f[t[u]]; // 跳到上一条链
            }
        }
    };
    

    主算法流程

    1. 构建能力树

    vector<vector<int>> g(n);  // 能力树
    for (auto [u, v] : e)      // 原树边
        g[s[u]].emplace_back(s[v]);  // 按能力值重建
    

    2. 计算自然晋升顺序

    priority_queue<int> q;
    q.emplace(s[0]);  // 根节点(能力最高的总经理)
    while (!q.empty()) {
        int u = q.top(); q.pop();
        o.emplace_back(u);  // 记录晋升顺序
        for (int i : g[u])  // 所有下属
            q.emplace(i);
    }
    

    3. 计算取消影响

    for (int i = 0; i < n; i++) {
        int u = o[i];  // 当前考虑的员工
        h.update(u, -I);  // 标记该员工被"阻碍"
        r[u] = i - max(0, h.query());  // 计算提前天数
        h.update(u, I + 1);  // 恢复
    }
    

    关键公式

    对于员工 uu 在自然顺序中的位置 ii

    • 如果没有取消:需要 ii
    • 最优取消后:需要 max(0,查询结果)max(0, \text{查询结果})
    • 提前天数:i最优取消后的天数i - \text{最优取消后的天数}

    样例解析

    样例输入

    5 4
    1 2 2 1      # p2=1, p3=2, p4=2, p5=1
    3 5 1 2 4    # 能力值
    5 3 1 4      # 查询的员工
    

    能力树构建

    原树:

        1(s=3)
       / \
      2   5(s=4)
     / \
    3   4(s=2)
    (s=5) (s=1)
    

    能力树(按能力值):

    根: 5 (最高能力)
    下属: 3, 4, 1, 2
    

    计算过程

    1. 自然晋升顺序:5, 3, 4, 1, 2

    2. 对于员工5(能力4):

      • 自然位置:第3个(从0开始?实际代码中可能调整)
      • 取消员工2后,只需1天
      • 输出:1
    3. 对于员工3(能力5):

      • 自然位置:第0个(已经是总经理)
      • 输出:0

    其他员工类似计算。


    复杂度分析

    • 构建能力树O(n)O(n)
    • 计算晋升顺序O(nlogn)O(n \log n)(堆操作)
    • 重链剖分O(n)O(n)
    • 处理每个员工O(log2n)O(\log^2 n)(重链剖分更新)
    • 总复杂度O(nlog2n)O(n \log^2 n)

    对于 n300,000n \leq 300,000 可以接受。


    算法亮点

    1. 能力树转换:将组织结构转化为按能力排序的树
    2. 重链剖分:高效处理树上路径查询和更新
    3. 线段树维护:动态计算阻碍影响
    4. 堆模拟:确定自然晋升顺序
    5. 独立查询处理:预处理所有可能情况,O(1)O(1) 回答查询

    总结

    这道题的核心在于:

    1. 问题转化:将晋升问题转化为树上的路径问题
    2. 数据结构:重链剖分 + 线段树处理动态路径查询
    3. 预处理:计算所有员工在最优取消下的结果
    4. 高效查询O(1)O(1) 回答每个查询

    这种"重链剖分 + 预处理"的方法在解决树上路径相关问题时非常有效,特别是在需要处理多个查询的场景下。

    • 1

    信息

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