2023-2024 ICPC Asia Jakarta Regional Contest K. Deck-Building Game
题目链接:https://codeforces.com/contest/1906/problem/K
题目大意:在原数组找出两堆异或值相同的数值,问有多少种找法,可以为空,每个数字可以不在任何一堆,两堆有标号。
所有做法的基础
一个显然的事情,这道题目相当于求 $\prod\limits_{i=1}^n(1+2x^{a_i})$ ,这里的乘法是异或卷积。
为了快速计算这个乘积,有了很多种搞法。
题解做法
题解做法的优点就是比较自然。
显然这个可以分治 FWT ,但是 FWT 不同于 FFT ,分治了值域不变,不改变复杂度。
咋整,观察到如果分治区间是 $[l,r)$ ,那么实际上这个区间乘出来的非 $0$ 项只能落在 $[0,r-l),[l,r)$ ,直接拿出左区间的两个非 $0$ 区间和右区间的两个非 $0$ 区间互相乘一下就行了,这样能做到时间复杂度:$O(V\log^2 V)$ 。
我的做法
我的做法和题解想法基本一致,唯一一点不同的是,对于 $(r-l)=2^l$ ,那么二进制下 $l$ 位都是一样的,因此对于二进制位剩下的位置,要么和 $[l,r)$ 里面的每个数字一样,代表异或了奇数次,要么全是 $0$ ,代表异或了偶数次。
所以我们不妨给每个数字的最高位填个 $1$ ,代表了这个数字异或次数的奇偶性即可。
时间复杂度仍然是:$O(V\log^2 V)$ 。
1 |
|
我的做法改进
发现一个事情,矩阵为 $\begin{bmatrix} 1 & 1\ 1 & -1 \end{bmatrix}$ 的 FWT (即异或的 FWT)最终的结果其实可以写成这样:
利用这个式子,可以改进分治 FWT 。
不妨假设:$(r-l)=2^l$ ,显然左区间的值域在 $[0,2^l)$ (高位补了 $1$ 判断奇偶性),现在要扩充到 $[0,2^{l+1})$ ,扩充规则为给原来的每个下表在最高位(算前导 $0$ )下面塞个 $0$ (右区间塞 $1$) ,然后在剩下的位置补 $0$ 。
然后根据上面的式子可以利用变化前的点值,在线性时间得到变化后的点值。(具体见代码)
然后直接乘就行了,时间复杂度:$O(V\log{V})$ 。
这个做法相比较于下面的做法,或许不是最妙的,但是是最适用的,因为不依赖于系数。
1 |
|
深刻观察法
来自比赛 Announcement 评论区。
发现一个事情,矩阵为 $\begin{bmatrix} 1 & 1\ 1 & -1 \end{bmatrix}$ 的 FWT (即异或的 FWT)最终的结果其实可以写成这样:
同时又观察到,结果只能是 $-1$ 或者 $3$ ,那么这有什么用呢?
思考一下,FWT 和 FFT 有一个很重要的不同,就是 FWT 不需要扩展数组,因为下标值域不会扩展,所以 FWT 实际上能算出所有多项式的点值表达式直接乘起来然后再逆回去,而 FFT 是不行的(除非一开始就把所有的多项式算出充足的点值)。
但是算出所有多项式的点值表达式的时间开销仍然很大,第一种做法采用了分治 FWT 来加速这个过程,但是这里,我们直接观察出了 FWT 后的结果长啥样,那我们是不是可以不用 FWT ,直接算出结果呢?
显然,求 $\prod f_k[i]$ 只需要算出有多少个 $-1$ 或者有多少个 $3$ 就行了。
有了这个思路,就有很多种搞法了。
我使用的做法是:$n=even+odd$ ,那么只需要令 $a[x]=x的出现次数$ ,然后直接跑 FWT ,就可以知道每个位置的 $even-odd$ ,然后就可以直接算出来 $-1$ 和 $3$ 的个数了。
还有别的搞法,例如:SOS dp,但是因为感觉这个的转移式子和 FWT 没什么本质区别,就不再赘述了,放个这个做法的代码: https://codeforces.com/contest/1906/submission/235477273 。
时间复杂度:$O(V\log{V})$ 的,空间复杂度:$O(V)$,$V$ 是值域。
1 |
|