题解 P9057【[Ynoi2004] rpfrdtzls】
题意
有 $n$ 个序列 $a_{1\dots n}$,有一个常数 $A$,初始时每个序列中有一个 $A+1$。对于序列 $s$,定义 $f(s)$ 表示最小的满足 $\prod_{i=1}^ks_i>A$ 的 $k$。有 $q$ 次操作,有两种操作:
- 对 $i\in[l,r]$,在序列 $a_i$ 前端加入一个数 $v$;
- 查询 $\sum_{i=l}^rf(a_i)$。
对每个 $2$ 操作输出答案。
$n,q\le 5\times10^5$,$1\le v,A\le10^9$。
思路
区间查询差分成 $l-1,r$ 两处的前缀查询。
考虑一个非常神秘的扫描线,对于第 $t$ 个操作(记作时刻 $t$ 的操作),如果是修改,在 $l_t$ 处插入 $(t,v_t)$,$r_t+1$ 处删除 $(t,v_t)$,并支持动态查询 $t$ 时刻的,当前前缀的答案。
考虑设扫到 $i$,现在集合内所有点对按 $t$ 排序,就是 $a_i$ 最终的形态(反过来),对于 $a_i$,$t_0$ 时刻的答案就是找到 $t_0$ 在集合中的前驱 $t_1$,往前连乘多少个会大于 $A$,记作 $f_{t_1}$。记 $pr_t,nx_t$ 分别表示存在于集合内的 $t$ 的前驱与后继,则 $[t,nx_t-1]$ 之间的答案都是 $f_t$。
考虑涉及乘法,我们会想到把 $v=1$ 和 $v\ne 1$ 分开计算。我们对每个 $v\ne 1$ 的 $(t,v_t)$ 维护 $(pr_t,v_{pr_t})$ 之间有多少个 $1$,记为 $c_t$。注意到最后被插入的若干个 $1$ 是没有算入的,这个在开头写一个区间加一,区间推平为零,区间求和的线段树 $T_1$ 即可。
现在集合里只维护了 $v\ne 1$ 的操作,可以发现,只用乘 $O(\log A)$ 个数就会超过 $A$,把这些乘到的结点的 $c+1$ 加起来就是此处的 $f$。
我们需要维护一个答案序列 $b$,$b_t$ 为当前前缀在时刻 $t$ 的答案总和。对集合里每个结点也维护 $s_t$ 表示对答案序列区间 $[t,nx_t-1]$ 还有 $s_t$ 的贡献没计入。查询时找到查询时刻 $t$ 的前驱,取出其 $s$ 值并加上 $b_t$ 即为所求。
我们不能对每个序列 $a_i$ 都遍历整个集合并将 $f_t$ 加入 $s_t$ 中。对于一些 $f$ 没有改变的结点,我们记录上一次加入答案的时刻 $tim_t$(指最后在扫到 $a_{tim_t}$ 这个序列时加入过),注意到 $f$ 不变,则在序列 $a_i$ 更新 $f$ 值的时候需要把 $s_t$ 增大 $(i-tim_t)f_t$,更新 $tim_t=i$。
在 $nx_t$ 发生改变时,我们需要把 $s_t$ 贡献到答案序列中,即将区间 $[t,nx_t-1]$ 加 $s_t$,将 $s_t$ 置零,需要支持的是单点查询,维护区间加单点求值的树状数组 $T_2$ 即可。
考虑把 $(t,v_t)$ 插入到 $(l,v_l)$ 和 $(r,v_r)$ 之间。我们需要先把 $s_l$ 贡献到答案序列中,更新 $l,t,r$ 的 $pr,nx$,还要更新 $c_t,c_r$,这需要查询 $[l+1,t-1]$ 间的 $1$ 的个数,写个单点加区间求和树状数组 $T_3$ 即可。
考虑删除 $(t,v_t)$,设 $l=pr_t,r=nx_t$,我们先把 $s_l,s_t$ 贡献到答案序列中,改变 $l,r$ 的 $pr,nx$,把 $c_r$ 加上 $c_t$ 即可。
所述集合可以用平衡树(set)简单维护,其实是维护了一个支持随机插入的链表。
时间复杂度是 $O(n+m(\log n+\log m+\log A))$。
需要代码的还是私信吧。
再见 qwq~

浙公网安备 33010602011771号