1 条题解

  • 0
    @ 2026-4-5 15:03:54

    Order Statistics 题解

    一、题目回顾(精简版)

    给定数组 a1,,ana_1,\dots,a_n,对参数 m,km,k 执行 mm 次操作: 每次选出当前最大的 kk 个元素(值相同则下标小更大),把它们1-1

    定义:

    • Fm,k(x)F_{m,k}(x):操作后数组非降序排序的第 xx 小值(第 xx 阶统计量)。
    • Sm,k(l,r)=x=lrFm,k(x)S_{m,k}(l,r)=\sum_{x=l}^r F_{m,k}(x)

    要求:

    1. 先输出 Fm0,k0(1..n)F_{m_0,k_0}(1..n)
    2. 处理 qq 个询问:
      • 单点修改 apva_p\leftarrow v
      • 查询 Fm,k(x)F_{m,k}(x)
      • 查询 Sm,k(l,r)S_{m,k}(l,r)

    所有查询独立计算,不改变数组;修改永久生效。


    二、核心观察(决定整个算法)

    1. 操作等价描述

    执行 mm 轮、每轮减 kk 个最大值,等价于:

    对每个位置 ii,设它被减了 tit_i 次,则

    i=1nti=mk\sum_{i=1}^n t_i = m\cdot k

    且满足单调性约束: 若 ai>aja_i > a_j(ai=aji<j)(a_i=a_j\land i<j),则 titjt_i\ge t_j

    2. 最终值形式

    最终数组中元素 ii 的值为:

    bi=aitib_i = a_i - t_i

    3. 关键转化:二分阈值

    存在一个阈值 VV,使得:

    • 满足 aitiVa_i - t_i \ge V 的元素数量 k\le k
    • 满足 aiti>Va_i - t_i > V 的元素数量 <k<k

    我们可以二分答案 VV,判断:

    最多能执行多少次“减最大 kk 个”操作,使得所有 biVb_i\ge V

    这就是标程中 chk(t,m) 函数的本质。


    三、核心函数:chk(t, m) 含义

    定义:

    $$\mathrm{chk}(V, m) = \sum_{a_i \ge V} \min(m,\ a_i - V) + \sum_{V-1 < a_i < V} (a_i - (V-1)) $$

    直观意义:

    把所有数“压到不低于 VV”,至少需要减掉多少总量。

    我们要二分最小的 VV,满足

    chk(V,m)mk\mathrm{chk}(V, m) \le m\cdot k

    这个 VV 就是最终数组的分界值,大部分数会落在 VVV1V-1


    四、最终数组结构

    二分得到分界值 tt 后,最终数组 bib_i 只有三类:

    1. bi=t+mb_i = t + m:极大值,被减满 mm 次。
    2. bi=tb_i = t:中间层,恰好减到阈值。
    3. bi=t1b_i = t-1:底层,减到阈值下一层。
    4. 更小的数:原本就远小于阈值,不受影响。

    标程中 mlen 就是恰好等于 tt 的元素个数


    五、求阶统计量与区间和

    我们需要:

    • 对最终 bb 数组排序;
    • 求第 xx 小;
    • 求区间 [l,r][l,r] 的和。

    1. 排序后取值分布

    排序后数组从低到高:

    • 一堆 <t1<t-1 的数;
    • 一堆 t1t-1
    • 一堆 tt
    • 一堆 >t>t 的数。

    2. 快速统计:树状数组 BIT

    离散化所有 aia_i 与修改值,维护两颗 BIT:

    • cnt\mathrm{cnt}:统计值域上的元素个数
    • val\mathrm{val}:统计值域上的元素和

    这样可以 O(logM)O(\log M) 完成:

    • 个数前缀和 \rightarrow 二分找第 kk 小;
    • 和前缀和 \rightarrow 区间和。

    这就是标程 query_ksolve 的作用。


    六、整体流程

    步骤 1:离线离散化

    把所有初始 aia_i 和所有修改操作的新值收集起来,离散化,压缩值域。

    步骤 2:初始化 BIT

    • cnt.add(rk(ai),1)\mathrm{cnt.add(rk(a_i), 1)}
    • val.add(rk(ai),ai)\mathrm{val.add(rk(a_i), a_i)}

    步骤 3:处理初始输出 Fm0,k0(1..n)F_{m_0,k_0}(1..n)

    对每个 x[1,n]x\in[1,n]

    • 二分阈值 tt
    • solve\mathrm{solve} 计算排序后第 xx 小值;
    • 输出。

    步骤 4:处理询问

    • 修改操作:在 BIT 中删去旧值,加入新值。
    • 查询 1/3:调用 work(m,k,l,r)\mathrm{work}(m,k,l,r),返回排序后 [l,r][l,r] 的和。

    七、关键函数公式化

    1. 二分阈值

    ll L = -INF, R = max_a;
    while (L < R) {
        ll mid = (L + R) / 2;
        if (chk(mid, m) > m * k) L = mid + 1;
        else R = mid;
    }
    

    对应数学条件:

    $$\mathrm{chk}(V, m) \le mk \quad\Rightarrow\quad \text{合法} $$

    2. 统计第 k 小

    ll qry(int l, int r, ll d)
    

    表示:

    值域 [l,r][l,r] 中,取最大的 dd 个数的和

    利用 BIT 二分实现。

    3. 总答案

    $$S(l,r) = \mathrm{solve}(t,m,mlen, n-l+1) - \mathrm{solve}(t,m,mlen, n-r) $$

    即排序后后缀和相减得到 [l,r][l,r] 的和。


    八、复杂度分析

    • 离散化:O((n+q)log(n+q))O((n+q)\log(n+q))
    • 单次询问 work\mathrm{work}
      • 二分 VVO(logA), A2109O(\log A),\ A\le 2\cdot10^9
      • 每次 chk\mathrm{chk}O(logM)O(\log M)
      • 统计 solve\mathrm{solve}O(logM)O(\log M)
    • 总体:O((n+q)logAlogM)O((n+q)\log A \log M) 可轻松通过 n,q2105n,q\le 2\cdot10^5,时限 4s4s
    • 1

    信息

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