1 条题解
-
0
题意重述
给定序列 。
$$\text{ans}(l,r) = \sum_{l \le s \le t \le r} \min_{s \le k \le t} a_k $$
对于每个询问 ,要求:即:区间 的所有子区间的最小值之和。
有 个询问, 很大(可达 ), 可达 ,因此必须用 或 时间 回答每个询问,预处理复杂度也要尽量低。
第一步:转化为历史问题
对于固定 ,设
则询问是求 。
我们考虑对右端点 递增处理,维护当右端点为 时,所有左端点 的答案 。
第二步:从 到
当右端点从 增加到 时,新增的子区间是所有 ()。
对于固定的左端点 , 相比于 增加了:
这个新增量不好直接加,我们换一种方式。
第三步:用单调栈维护最小值控制区间
经典问题:对于每个 ,我们要快速知道以 为右端点的所有子区间的最小值之和。
设 表示以 为右端点的所有子区间的最小值之和:
那么显然:
$$F_r(l) = \sum_{t=l}^{r} g(t) - \sum_{t=l}^{r-1} g(t) \text{ 的某种前缀和} $$更直接地,我们可以直接维护 ,那么:
$$\text{ans}(l,r) = \sum_{t=l}^{r} g(t) \quad\text{???} $$不对,这样会多算。让我们仔细推导。
正确推导:
$$\text{ans}(l,r) = \sum_{t=l}^{r} \sum_{s=l}^{t} \min_{s \le k \le t} a_k $$
$\text{ans}(l,r) = \sum_{l \le s \le t \le r} \min_{s\le k\le t} a_k$。
交换求和顺序,先固定右端点 :定义 ,则:
$$\text{ans}(l,r) = \sum_{t=l}^{r} \sum_{s=l}^{t} h(t,s) $$那么如果我们能对每个 维护 ,就可以用 求出第二层和。
但维护 对所有 不现实。
第四步:转化为前缀和差分
设 ,即整个前缀 的所有子区间最小值之和。
那么:
$$\text{ans}(l,r) = P(r) - P(l-1) - \text{“左端点在 $l$ 左边且右端点在 $l$ 到 $r$ 的子区间的最小值之和”} $$这个“多余部分”不好直接减。
更常见的方法:莫队 或 分治 在 很大时不行,必须 查询。
第五步:经典算法:离线 + 单调栈 + 区间历史和维护
已知一个经典解法(类似“所有子区间最小值之和”的拓展):
对于每个位置 ,考虑 作为最小值的影响区间。
设 是 左边第一个 的位置(不存在则为 ),
是 右边第一个 的位置(不存在则为 )。那么 作为最小值的区间 必须满足 。
对询问的贡献
对于一个询问 , 的贡献是:
- 当 且 在 内时, 是该区间的最小值。
- 符合条件的 对数 = $(i - \max(l, L_i+1) + 1) \times (\min(r, R_i-1) - i + 1)$,如果这个值 。
因此:
$$\text{ans}(l,r) = \sum_{i=1}^{n} a_i \cdot \max(0, i - \max(l, L_i+1) + 1) \cdot \max(0, \min(r, R_i-1) - i + 1) $$这个式子可以拆成多个部分,用 二维前缀和 或 扫描线 + 数据结构 处理。
第六步:具体拆分
令 ,。
则 对询问 有贡献的条件:- (自动满足 )
贡献值为 $a_i \cdot (i - \max(l, A_i) + 1) \cdot (\min(r, B_i) - i + 1)$。
拆成四种情况(根据 与 、 与 的关系):
- 且 :贡献 =
- 且 :贡献 =
- 且 :贡献 =
- 且 :贡献 =
这四种情况分别对应 落在某个二维平面的某个矩形区域。
我们可以把询问 看作平面上的点,每个 贡献是这些区域上的 乘以一个关于 的一次或二次函数。
第七步:用二维差分累加
以情况 4 为例:条件 且 ,且 。
贡献 =
= $a_i \cdot [ (i+1)(-i+1) + (i+1)r + (-i+1)(-l) + r(-l) ]$
实际上展开: $(i-l+1)(r-i+1) = (i+1)(-i+1) + (i+1)r + l(i-1) - lr$
更规范展开:再展开 ,仍复杂。
更好的办法:我们直接维护 和 的乘积项、一次项、常数项系数。
令: 贡献
$= a_i \cdot [ (i+1)r - (i+1)i + i+1 - lr + l i - l ]$所以:
- 项系数:
- 项系数:
- 项系数:
- 常数项: 先不细算,反正可以统一。
类似地,其他情况也可拆成 的线性组合。
这样,我们可以对二维平面做 矩形加一个多项式,最后询问时求该点的多项式值。
矩形加多项式可以用 二维差分(四阶差分)实现。
第八步:算法步骤
- 用单调栈 求出 。
- 对每个 ,根据 ,确定四个矩形区域(对应上述四种情况),在每个矩形上对系数数组 做差分修改。
- 做完所有 的更新后,对系数数组做二维前缀和,得到每个 点的四个系数。
- 生成询问 (用给定随机函数),计算 $ans(l,r) = c_{lr}[l][r] \cdot lr + c_l[l][r] \cdot l + c_r[l][r] \cdot r + c_1[l][r]$。
- 累加所有询问答案,取模 。
第九步:复杂度分析
- 预处理 :
- 每个 更新四个矩形:每个矩形 差分修改(二维差分需在矩形四个角加减)
- 二维前缀和:?不行, 太大(),不能开 数组。
第十步:优化空间
由于 很大但 也很大,不能开 数组。
必须在线回答询问,即给定 能快速计算答案。注意到每个 贡献是一个分段函数,询问 时可以快速求和:
$$\text{ans}(l,r) = \sum_{i=l}^{r} a_i \cdot (i - \max(l, A_i) + 1) \cdot (\min(r, B_i) - i + 1) $$
我们可以将贡献公式写成:这个求和可以通过预处理一些前缀和,在 内完成。
具体:
- 对固定的 ,当 从 到 变化时, 要么是 ,要么是 ,分界点在 的位置。
- 类似地, 要么是 ,要么是 。
因此可以拆成至多四个部分,每部分内 满足固定 和 ,贡献可以写成 ,展开后是 的一次和二次前缀和。
第十一步:实现方案
预处理:
- 对每个 ,存 。
- 预处理 、、 等前缀和。
- 对每个可能的 ,需要快速找到 满足 的分界点,这可以通过对 排序并建立数据结构(如可持久化线段树)实现。
但由于 很大且 很大, 每个询问可能仍太慢( 约 操作,勉强可过)。
第十二步:最终策略
综合以上,最优做法:
- 离线所有询问(用生成器生成)。
- 用扫描线按 递增处理询问,同时维护一个数据结构支持区间加一次函数、区间查询和。
- 当右端点 增加时,更新所有 的 的贡献。
- 用线段树维护当前 下每个 的答案 ,支持区间加二次函数(关于 )。
这样每个 在 从 到 期间贡献一个关于 的一次函数,加到线段树区间 上。
每个询问就是查询线段树位置 的值。
复杂度:, 时 约 ,总操作 ,在优化良好的 C++ 中可过。
总结
本题难点:
- 询问次数极大,需 或更快回答。
- 将区间子区间最小值求和转化为每个位置作为最小值的贡献。
- 利用单调栈求控制区间。
- 通过差分或扫描线+数据结构处理二维贡献。
最终方案:扫描右端点,用线段树维护左端点答案,区间加一次函数,实现 。
- 1
信息
- ID
- 5918
- 时间
- 4000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 5
- 已通过
- 1
- 上传者