题解 P8146【[JRKSJ R4] risrqnis】
题意
给你一个长度为 $n$ 的序列 $a$,有 $q$ 次操作,初始有 $m$ 个空集 $S$,共有两种操作:
1 l r k,$S_k\gets S_k\cup\{x|l\le x\le r\}$;2 l r k,查询 $\displaystyle\sum_{i=l}^r[a_i\in S_k]$。
数据范围:
- 对于 $30\%$ 的数据,$n,q\le10^6$,$m=1$;
- 对于 $70\%$ 的数据,$n,q\le 3\times 10^5$,$m\le 10^9$。
题解
$\text{Algorithm 1}$
称所有 $a_i\in S$ 的 $i$ 的数 $i$ 为“合法结点”。
考虑算出每次插入操作后每个数是不是合法结点,查询时统计区间的合法结点数的个数即可。
考虑将原序列排序,得到序列 $b$,对于一次插入操作,在 $b$ 上二分找到第一个 $\ge l$ 的数 $L$,最后一个 $\le r$ 的数 $R$,将 $[L,R]$ 中的数的原位置标记为合法结点。考虑使用树状数组,标记合法结点后可以直接查询。
现在遇到了一个问题:一个数可能会被多次标记合法结点,导致复杂度出错。
考虑使用并查集惰性删除的简单 trick,删除一个数 $i$ 时,将集合 $i$ 合并至集合 $i+1$,对于一个本次新标记的点 $i$,下一个需要标记的点为 $\text{find}(i+1)$。容易发现,这样每个数都不会被重复标记。
由于每个数都只会被标记一次(可以看作势能分析),时间复杂度为 $O((n+q)\log n)$,期望得分 $30\text{pts}$。
有 $m$ 个集合时,时间复杂度实质上是 $O((nm+q)\log n)$。
$\text{Algorithm 2}$
很显然,$m$ 是不重要的,因为显然可以离散化至 $O(q)$。
令 $m=q=O(n)$。
首先可以发现每个集合的操作是互不影响的,可以分别考虑。
需要进行 $O(n)$ 组以下的操作:
- 将值域中 $[l,r]$ 间的数的权值推平 $1$
- 询问编号 $[l,r]$ 间数的权值和
先用 set 维护颜色段均摊(ODT)将区间推平转为区间加,可以发现转完的操作数是 $O(n)$ 的。
考虑序列分块,每个块维护一个值域上的前缀和,整块打标记很好打,只需要将答案加上 $pre_r-pre_{l-1}$ 即可。考虑散块怎么做,问题即为区间加单点求值,注意到要 $O(1)$ 查询,不能用权值树状数组之流,于是拿个值域分块过来直接做就行了。
于是乎得到了一个时空 $O(n\sqrt n)$ 的算法,但这样你只能得 $30\text{pts}$,因为空间不够优秀。
考虑降空间复杂度至 $O(n)$。考虑 simple trick 离线逐块处理。直接逐块处理散块时复杂度可能会退化为 $O(n^2)$,注意将整块贡献与散块贡献分开计算。整块贡献要先枚举块再枚举集合,散块贡献很 simple。
如果你被卡常,过不了,可以尝试调一个合适的块长。
得到了一个时间 $O(n\sqrt n)$,空间 $O(n)$ 的算法。期望得分 $70\text{pts}$。
将两部分结合起来即可。
代码实现可以找我要。
$\text{Bonus}$
有根号分治做法,也可以线性空间,具体看 $\texttt{abruce}$ 题解。
跑得比序列分块快多了。
再见 qwq~

浙公网安备 33010602011771号