推一手蒟蒻 $\color{limegreen}{\texttt {博客}}$ ~
抓住CF爆炸的间隙乘机AC的水题。(滑稽
做这道题只需要知道两个点:
异或前缀性 和 普通莫队算法 。
不妨分开讲。
$\large\color{salmon}{\texttt {异或前缀性:}}$
前缀和可以 $O(1)$ 求解区间和。
前缀积可以 $O(1)$ 求解区间积。
那前缀异或能否 $O(1)$ 求解区间异或呢?
答案是可以的。
$\color{violet}{\texttt {尝试证明:}}$
我们设 $num$ 为一段序列, $xornum_{\small\ l,r}$ 表示这段序列里第 $l$ 到第 $r$ 个元素异或的结果。
手模可知,异或操作满足 结合律 。
把 $xornum_{\small\ 1,l}$ 看成 $a$ , $xornum_{\small\ l,r}$ 看成 $b$ 和 $c$ 异或的结果,那我们也就有了:
我们又知道异或有以下两个性质:
那么我们可以经历一番移位得出:
佐佑同时异或上一个 $xornum_{\small\ 1,l}$ ,再套用一个结合律得:
用上面两个性质一通转换:
这样,我们就可以 预处理 异或前缀,来 $O(1)$ 求出区间异或的结果~
$\large\color{salmon}{\texttt {普通莫队算法:}}$
定一道莫队题需要看三眼。
第一眼,奇怪的区间查询。
第二眼,可以离线。
第三眼,$N\sqrt{N}$ 能过。
完全符合条件,那就先把莫队的板子敲好。
(这里默认读者一定是会打莫队的QwQ。)
思维难度在 $ins/add$ 和 $del$ 两个函数上。(即 区间扩张/收缩 时的 添/删元素 )
$\color{violet}{\texttt {如何定制这两个函数?}}$
这里要查的是异或值 $=k$ 的元素对数。
两个数 $a,b$ 做异或,知道其中的一个数 $a$ ,知道异或的结果 $c$ ,则运算的另一个数也是 确定的 ,即 $a\oplus b=c\Leftrightarrow b=a\oplus c$ 。(这也可以用于理解异或的前缀性)
我们增添一个元素 $p$ 进入区间时,它带有一个异或前缀 $xornum_{\small\ 1,p}$ ,查一下区间里 和 $xornum_{\small\ 1,p}$ 异或结果 为 $k$ 的异或前缀的出现次数。
也就是说,我们需要开一个 $cnt$ 数组,维护每个 异或前缀值 当前出现的次数。
参考之前得到的式子,可以推出和 $xornum_{\small\ 1,p}$ 异或得 $k$ 的数,即为 $xornum_{\small\ 1,p}\oplus k$ 。
那么添加一个元素 $p$ 时就应在答案上加上 $cnt[xornum_{\small\ 1,p}\oplus k]$ 。
同理,删除一个元素 $p$ 就应减去 $cnt[xornum_{\small\ 1,p}\oplus k]$ 。
$\color{violet}{\texttt {为什么能保证不重不漏?}}$
因为 $a\oplus b$ 等于 $b\oplus a$ ,可以保证区间中任取两个数,他们之间只计算了一次。
$\color{salmon}{\texttt {实现注意:}}$
$cnt$ 数组的大小应为 异或前缀的值域 大小,而非序列中元素个数,这里应为 $2\times 10^6$ 。
正如我们做闭区间和时的 $sum[r]-sum[l-1]$ 一样,这里的闭区间异或也应为 $xornum[r]\oplus xornum[l-1]$ ,一种省力的方法是在输入的时候就把 所有的 $l$ 都减去 $1$ 。
$\color{salmon}{\texttt {Code:}}$
1 |
|