建议先完成 课前零食:把相同的一段装进一个盒子。它从段卡、split 边界和一个故意构造的坏例子出发,避免把 ODT 误当成万能的均摊对数结构。
理解有序区间树(Ordered Disjoint Tree, ODT/Chtholly Tree)的真实适用条件:它维护等值段,依靠随机数据中的区间赋值压缩段数;能说明其期望性能来源,也能构造对抗数据证明它不是通用平衡树模板。
前置:std::set、迭代器失效、快速幂、随机生成器。
四类操作无法由常规固定摘要自然统一;题面把操作全部随机生成,是在邀请我们把“等值段数量的期望受控”纳入复杂度分析,而不是寻找一个虚构的最坏情况对数模板。
$n,m\le10^5$。数组和全部操作由给定线性同余伪随机生成器产生;支持区间加、区间赋值、区间第 $k$ 小、区间幂和模任意 $y$。
逐点操作最坏 $O(nm)$。线段树能处理区间加/赋值,但“第 $k$ 小”和“任意临时指数、临时模数的幂和”没有小而稳定的合并摘要。题面却保证所有数据与操作按固定随机序列生成,且四种操作均匀出现;频繁的随机区间赋值会删除大量旧断点。
ODT 维护当前等值段。统一切出 [l,r] 后:
value;[l,r,x];(value,length),按值排序后扣长度;length * value^x mod y。单次耗时与被访问段数成正比(第 $k$ 小再乘排序对数)。随机赋值持续把长区间压成一个段,使期望总访问量可接受。
不变量是 set 中各段按左端点严格递增、互不重叠、并集为 $[1,n]$,且段内数组值相同。split 只把一段拆成两个同值段,不改变数组;之后 [split(l),split(r+1)) 恰覆盖查询区间。
加法逐段修改与逐元素加等价;赋值删除恰好覆盖范围并插入统一值;查询中每段长度等于该值在区间贡献的元素数。因此四种操作都保持不变量并返回精确答案。
确定性最坏复杂度仍可达 $O(nm)$:交替制造大量单点不同值,再查询整段即可扫描 $\Theta(n)$ 段。这里通过的依据是题目指定的伪随机生成过程与大量随机区间赋值带来的期望段数控制,不可写成“ODT 保证均摊 $O(\log n)$”。
split(pos):
find interval [l,r,v] containing pos
if l == pos: return it
replace it by [l,pos-1,v] and [pos,r,v]
return iterator of [pos,r,v]
operate(l,r):
end = split(r+1)
begin = split(l)
# [begin,end) is exactly the affected interval list
ODT 与平衡树的联系只在于用有序集合维护断点;它不维护树形聚合信息。分块也能利用随机赋值重构块,但性能模型与实现边界不同。
l 比较;r,value 必须可通过 const 迭代器修改,常用 mutable。end=split(r+1),再取得 begin=split(l),随后整体 erase(begin,end)。rnd() 的调用次数;第 3 类和其他操作生成 x 的取模不同,第 4 类还多生成 y。y;本题 y\le10^9,64 位乘法足够。写出一个让段数长期保持 $\Theta(n)$ 的操作序列;说明 split 为何不改变语义;解释这题的随机性为何属于复杂度契约。
用小数组逐点模拟对拍四类操作,并额外记录每步段数观察赋值的压缩效果。卡在接口实现时先单测 split(1)、split(n+1) 与连续赋值。