题解:P6749 『MdOI R3』Yoshino

· · 题解

首先因为等差数列公差为 1,所以内部没用贡献,非 [l, r] 区间分为两部分:L = [1, l-1]R = [r+1, n],贡献分四类计算:

贡献类型 范围 计算 含义
类型 1 左部元素 x 值域 (R, V]x > R cnt1 * len 左部每个 x 都比 X 中所有元素大,共 cnt1x,每个贡献 len 个逆序对
类型 2 右部元素 x 值域 [1, L)x < L cnt2 * len 右部每个 x 都比 X 中所有元素小,共 cnt2x,每个贡献 len 个逆序对
类型 3 左部元素 x 值域 [L, R]L ≤ x ≤ R x - L X 中比 x 小的元素有 x - L 个(XL, L+1, ..., R),每个 x 贡献 x - L
类型 4 右部元素 x 值域 [L, R]L ≤ x ≤ R R - x X 中比 x 大的元素有 R - x 个,每个 x 贡献 R - x

总贡献

\Delta = (\text{cnt1} + \text{cnt2}) \times \text{len} + (\text{sum3} - \text{cnt3} \times L) + (\text{cnt4} \times R - \text{sum4})

其中:

用 ODT 维护区间,用「分块+树状数组」维护「区间-值域」的个数和和,快速计算贡献公式中的 cntsum 查询。

查询的时候查询 [l, r] 中值域在 [L, R] 内的元素个数和和:

时间复杂度 O(n\sqrt n\log n)

虽然能过原题,但是这个复杂度还是太慢了,考虑修改的时间戳和逆序对形成三维偏序,考虑用树套树或 cdq 分治维护,因为覆盖是区间的所以考虑颜色段均摊。

把每个覆盖块看成一个点,考虑均摊的时候如何以低时间复杂度维护单次查询和更新。

考虑查询,对于每一个等差数列块 [l,r],我们要算每个 i\in[l,r] 块右边有多少值 \le a_i

我们设 c_i 表示 i 的个数,设 s_i 表示小于 i 的个数,设 f_i 表示 \sum_{j=1}^is_i 也就是小于等于 is_i 的和,那么对于每个块答案就是 f_r-f_{l-1},但是时间复杂度爆了,因为块的数量不是均摊的。

考虑查询是全局的,于是尝试反过来算修改对全局逆序对的贡献, 首先因为等差数列公差为 1,所以内部没用贡献, 对于区间 A[s,t]B[u,v]AB 左侧),贡献为:

\text{contribution}(A, B) = \sum_{x=s}^t \sum_{y=u}^v [x > y]

固定 x,内层求和 \sum_{y=u}^v [x > y] 的结果是 y < x 的数量,分三类:

x\le u0

u < x \le v+1x - u

x > v+1v - u + 1

因此总贡献可拆分为:

\text{contribution}(A, B) = \sum_{\substack{x = \max(s, u+1) \\ x \leq \min(t, v+1)}} (x - u) + \sum_{\substack{x = \max(s, v+2) \\ x \leq t}} (v - u + 1)

一次修改生成区间 [sv, tv],其对任意值 i 的贡献 f(i) 是分段函数:

0 & \text{if } i \leq sv, \\ i - sv & \text{if } sv < i \leq tv + 1, \\ tv - sv + 1 & \text{if } i > tv + 1. \end{cases}

为了 O(1) 表示 f(i),利用差分标记其变化率:

i=sv+1 处标记 +1;在 i = tv + 2 处标记 -1

对标记做两次前缀和即可还原 f(i)

查询时需计算值范围 [L,R] 的总贡献,即\sum_{i=L}^R f_i

g_i 表示 \sum_{j=1}^i f_j,答案就是 g_r-g_{l-1}

发现是一个三维前缀和,通过展开前缀和式子得:

然后就是一个树状数组维护多维前缀和的经典套路,开三个树状数组,分别维护 $x$,$i\times x$,$i^2\times x$。 cdq 分治时间复杂度 $O(n\log^2 n)$,颜色段均摊时间复杂度 $O(n+m)$。 总时间复杂度 $O(n\log^2 n)$。