想起来这玩意看懂了之后一直没写……
要不是 WJJ 今天问了我我还真就遗忘在任务列表里了(逃
思想
考虑类比 FFT 处理多项式乘法的方式,可以尝试设计一种变换,使得其能够将位运算卷积转化为对应位置乘积。
处理位运算卷积
按位或
对于序列 a,b,考虑序列 c,其中 ci=∑j|k=iajbk
考虑一个对于序列的变换 FWTor(a): FWTor(a)i=∑j|iaj
则注意到 FWTor(c)i=∑(j|k)|i=iajbk=∑j|i∑k|iajbk=FWTor(a)iFWTor(b)i
这显然正是我们设计这个变换而所期望得到的。
接下来考虑如何快速实现这个变换(O(n2) 肯定是接受不了的)。
考虑分治,每次将 a 数组按下标在二进制下的最高位分,即对半分。
设在 a 中下标最高位为 0 的部分为 a0,为 1 的部分为 a1。
则由于只有 0 对 1 产生贡献,而 0,1 均对 1 产生贡献,有 FWTor(a)=merge(FWTor(a0),FWTor(a0)+FWTor(a1))
其中两个序列的加法意义为对应位置相加,merge 为将两个序列首尾相接形成一个新的序列。
如此即可分治。
按位与
对于序列 a,b,考虑序列 c,其中 ci=∑j&k=iajbk
类似地,考虑 FWTand(a)i=∑j&i=iaj
显然 FWTand(c)i=FWTand(a)iFWTand(b)i。
类似地,0,1 都对 0 有贡献但只有 1 对 1 有贡献,故有 FWTand(a)i=merge(FWTand(a0)+FWTand(a1),FWTand(a1))
按位异或
对于序列 a,b,考虑序列 c,其中 ci=∑jxork=iajbk
定义 a⊕b=popcount(a&b)mod2,其中 popcount(x) 表示 x 在二进制表示下 1 的个数。
则显然有 (i⊕j)xor(i⊕k)=(jxork)⊕i。
考虑 FWTxor(a)i=∑i⊕j=0aj−∑i⊕j=1aj
则有 FWTxor(a)iFWTxor(b)i=(∑i⊕j=0aj−∑i⊕j=1aj)(∑i⊕k=0bk−∑i⊕k=1bk)=(∑i⊕j=0∑i⊕k=0ajbk+∑i⊕j=1∑i⊕k=1ajbk)−(∑i⊕j=0∑i⊕k=1ajbk+∑i⊕j=1∑i⊕k=0ajbk)=∑(i⊕j)xor(i⊕k)=0ajbk−∑(i⊕j)xor(i⊕k)=1ajbk=∑(jxork)⊕i=0ajbk−∑(jxork)⊕i=1ajbk=FWTxor(c)i
分治合并时考虑最高位会贡献 1 还是 −1,有 FWTxor(a)=merge(FWTxor(a0)+FWTxor(a1),FWTxor(a0)−FWTxor(a1))
逆变换
逆变换什么的,把 FWT 倒过来做就好啦(
反正都是自底向上的,还是比较好做的。
代码
1 |
|