1 条题解

  • 0
    @ 2025-8-24 23:08:56

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar LostKeyToReach
    争取今年不退役 || int_stl. || 有意互关私。

    搬运于2025-08-24 23:08:56,当前版本为作者最后更新于2025-01-28 08:55:32,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    本题解提供离线+树状数组+双指针做法,代码请根据思路自行实现。

    请务必读完解题思路。

    题目回顾

    给定 n,c,dn, c, d{a}\{a\},你有 qq 次询问,每次给出 l,rl, r,求:

    $$\sum_{l \le x \le y \le r}[c \le \text{inv}(x, y) \le d]. $$

    解题思路

    考虑转换原式。

    $$\sum_{l \le x \le y \le r}[c \le \text{inv}(x, y) \le d] = \sum_{l \le x \le y \le r} [\text{inv}(x, y) \le d] - \sum_{l \le x \le y \le r} [\text{inv}(x, y) \le c - 1]. $$

    接下来求解 $\displaystyle \sum_{l \le x \le y \le r} [\text{inv}(x, y) \le d]$。

    定义 Ld(r)=min{linv(l,r)d}L_{d}(r) = \min \{l \mid \text{inv}(l, r) \le d\}cntd(r)=rLd(r)+1\text{cnt}_{d}(r) = r - L_{d}(r) + 1psd(r)\text{ps}_{d}(r)cntd(r)\text{cnt}_{d}(r) 的前缀和。

    • Ld(r)L_{d}(r):表示以 rr 为右端点时最小的 ll 使得 [l,r][l, r] 区间的逆序对数量 d\le d,这可以使用「双指针 + 树状数组」的方法来实现,具体思路如下:

      • 维护一个窗口 [l,r][l, r] 及当前窗口的逆序对数量 CurrInv\text{CurrInv},再开一个树状数组用于维护逆序对数量。
      • 枚举 rr,每次加入 ara_r 时将 CurrInv\text{CurrInv} 加上「当前窗口里大于 ara_r 的数的个数」。
      • 如果 CurrInv>d\text{CurrInv} > d,就移动 ll 向左收缩,同时将 CurrInv\text{CurrInv} 减去「当前窗口里小于 ala_l 的数的个数」直到 CurrInvd\text{CurrInv} \le d
      • 操作结束后,ll 便为 Ld(r)L_{d}(r)
    • cntd(r)\text{cnt}_d(r):表示使得 inv(l,r)d\text{inv}(l, r) \le dll 的数量,将 Ld(r)L_d(r) 求解完毕后计算 rLd(r)+1r - L_d(r) + 1 即可。

    • psd(r)\text{ps}_d(r):为 cntd(r)\text{cnt}_d(r) 的前缀和,表示右端点为 1r1 \sim r 时,有多少个子区间满足 inv(l,r)d\text{inv}(l, r) \le d

    对于这部分,我们对 ddc1c - 1 分别进行一次这样的计算。

    至此,此部分求解完毕,接下来,我们思考一个问题:如何从上述信息,得到「区间 [l,r][l,r] 内,有多少子区间的逆序对 d\le d」?

    固定 rr,若我们只关心「右端点 = rr」的子区间,逆序对 d\le d 的那些,其左端点 xx 满足:

    xLd(r).x \ge L_d(r).

    故此时子区间数量为:

    rLd(r)+1=cntd(r).r - L_d(r) + 1 = \text{cnt}_d(r).

    是现在我们只想要「左端点 xlx \ge l 且右端点为 rr」的子区间,这些子区间的数量应该是:

    $$f_d(r, l) = \begin{cases} \text{cnt}_d(r), &\quad L_d(r) \ge l,\\ r - l + 1, &\quad L_d(r) < l. \\ \end{cases} $$

    那么我们再对 [l,r][l, r] 中的 fd(k,l)f_d(k, l) 求和,便可得区间 [l,r][l, r] 内、右端点为 rr 的所有子区间中满足逆序对 d\le d 的数量。

    $$\begin{aligned} \sum_{k = l} ^ r f_d(k, l) &= \sum_{k = l} ^ r \text{cnt}_d(k) - \sum_{k \in S}\left[\text{cnt}_d(k) - (k - l + 1)\right] \\ &=[\text{ps}_d(r) - \text{ps}_d(l - 1)] - \sum_{k \in S}[l - L_d(k)]. \end{aligned} $$

    其中 S={k[l,r]Ld(k)<l}S = \{k \in [l, r] \mid L_d(k) < l\}

    接下来引入两个变量 yl,ry_{l, r}zl,rz_{l, r} 用于求解 kS[lLd(k)]\displaystyle\sum_{k \in S}[l - L_d(k)]

    yl,r=k[l,r],Ld(k)<l1.y_{l, r} = \sum_{k \in [l, r], L_d(k) < l}1. $$z_{l, r} = \sum_{k \in [l, r], L_d(k) < l} L_d(k). $$

    此时 $\displaystyle\sum_{k \in S}[l - L_d(k)] = l \times y_{l, r} - z_{l, r}$。

    那么 yly_lzlz_l 该如何求解?我们发现 Ld(k)<lL_d(k) < l 这个条件的存在增加了题目难度,那我们不妨开一个桶 bbbxb_x 记录所有 Ld(k)=xL_d(k) = x 的位置 kk,处理询问时将 x<lx < lbxb_x 中的位置加入便可以方便地处理掉这个限制。

    接下来,我们开两个树状数组 fc\text{fc}fv\text{fv} 分别维护 yl,ry_{l, r}zl,rz_{l, r},加入 bxb_x 的位置时分别单点加 11Ld(k)L_d(k) 即可。

    同理,我们对 ddc1c - 1 分别做一次。

    最后,设我们处理出来的答案为 ansd(i)\text{ans}_d(i)ansc1(i)\text{ans}_{c - 1}(i),直接输出 ansd(i)ansc1(i)\text{ans}_d(i) - \text{ans}_{c - 1}(i) 即可。

    时间复杂度分析

    • 预处理:O(nlogn)O(n \log n)
    • 离线计算答案:O((n+q)logn)O((n + q) \log n)
    • 总时间复杂度:O((n+q)logn)O((n + q) \log n)

    可以轻松处理 n,q5×105n, q \le 5 \times 10^5 的数据。

    代码实现

    请自行实现。

    • 1

    信息

    ID
    10982
    时间
    1500ms
    内存
    512MiB
    难度
    5
    标签
    递交数
    0
    已通过
    0
    上传者