给出一个长度为 \(n\) 的序列,给出 \(m\) 次操作,要求支持区间赋值,区间求和,撤销区间赋值这三个操作,强制在线。
\(n,m\le 10^5\) ,赋值操作的次数不超过 \(65000\) 。
小清新数据结构题。
考虑一个暴力,在每个下标上维护一个栈,区间赋值就将这个区间的所有栈都加入这个操作,撤销时给撤销的操作打上撤销标记,那么对于一个下标,对应的栈将栈顶的被打上撤销操作的操作弹出后剩下的栈顶的操作的值就是当前的值,不妨称这样的操作是这个下标的栈顶操作,由于在一个下标的栈中,每个操作只会进出各一次,所以总复杂度是 \(\mathcal{O(n^2)}\) ,这个居然能拿到 2 分(
考虑来个分块,维护每个下标的栈和整块的栈,分别处理散块和整块。
先考虑整块,先将整块包含的下标的栈顶操作按照时间排序,假如没有遭到过散块操作,那么每次只会改变整块的栈顶操作的时间,很显然,这个块的当前值的总和是包含的下标中栈顶操作的时间大于整块的栈顶操作的时间的栈顶操作的值之和再加上剩下的操作的个数乘上整块的值,假设有 \(x\) 个下标的栈顶操作的时间小于整块的操作的时间,整块的操作的值为 \(v\) ,整块按照时间从小到大排序后从 \(x\) 开始的后缀的操作的值之和为 \(suf_x\),答案为 \(x*v+suf_{x+1}\) 。
假如遇到的操作是赋值,显然这个操作的时间大于这个块包含的所有操作的时间,那么区间和就是块的大小乘上这个操作的值。
如果遇到的是撤销,显然整块的操作的时间一定是单调下降的,可以单指针维护出答案。
注意赋值操作后不要把指针撤到最后面,这样复杂度就不对了,指针只需要维护之前的操作,因为后来的赋值操作一定比所有的时间都大。
假如遇到了散块修改就整块重构,因为赋值操作的次数不超过 \(65000\) ,重构时为了卡常可以按 \(2^8\) 进制搞基数排序。
每次赋值操作只会有两次散块操作,一次散块操作进行一次 \(\mathcal{O(\sqrt{n})}\) 的重构,而 \(\mathcal{O(\sqrt{n})}\) 次的整块操作的单次复杂度是 \(\mathcal{O(1)}\) 的,总复杂度为 \(\mathcal{O(n\sqrt{n})}\) 。