0. 引入与一些约定
想要快速计算
我们可以利用 \(\mathtt{FFT}\) 的思想:将系数多项式转化成点值多项式,\(\mathcal O(n)\) 求得答案后将其还原成系数多项式。
假设 \(A,B\) 均为多项式,则 \(A+B,A-B,A\times B\) 都代表着 对位运算。
\(\mid,\&,\oplus\) 分别表示或、与、异或。且若无特殊说明,\(i\subseteq j\) 表示在二进制意义下 \(i\) 是 \(j\) 的子集。
1. 按位或卷积
1.1. 定义与正确性
定义
所以有
按照定义将上面那个柿子 \(\mathtt{IFWT}\) 一下就是 \(A|B\) 了。
1.2. 正变换
对于一个多项式 \(A\),不妨设其长度为 \(2^n\),令 \(A_0,A_1\) 分别为前 \(2^{n-1}\) 项,后 \(2^{n-1}\) 项(其实也可以理解为将下标以二进制的最高位分组),有:
这个递归式可以这样理解:最终能贡献到前 \(2^{n-1}\) 项的项的最高位一定为零,此时 \(A_1\) 这一部分一定无用,所以由 \(\mathtt{FWT}(A_0)\) 组成;贡献到后 \(2^{n-1}\) 项的项的最高位可以为 \(0/1\),所以相当于没有限制,此时贡献到哪一位只由二进制的后 \(n-1\) 位决定,所以直接取 \(\mathtt{FWT}(A_0)+\mathtt{FWT}(A_1)\) 即可。
另外我们也可以得到 \(\mathtt{FWT}(A_0+A_1)=\mathtt{FWT}(A_0)+\mathtt{FWT}(A_1)\),考虑 \(j\) 对满足 \(j\subseteq i\) 的 \(i\) 的贡献即可。
1.3. 逆变换
\(n=0\) 的情况就不再赘述,对于 \(n>0\) 的情况,我们直接将 \(\mathtt{FWT}(A)\) 代入柿子:
1.4. 代码实现
void OR(int *f,int opt=1) {
for(int l=2;l<=n;l<<=1)
for(int o=0,p=l>>1;o<n;o+=l)
for(int i=o;i<o+p;++i)
f[i+p] = inc(f[i+p],f[i]*opt);
}
2. 按位与卷积
2.1. 定义
定义
所以有
按照定义将上面那个柿子 \(\mathtt{IFWT}\) 一下就是 \(A\&B\) 了(其实计算 \(\mathtt{FWT}(A\&B)\) 应该会更好理解,你会发现 \(\mathtt{FWT}(A\&B)[i]\) 就是枚举 \(i\) 的超集 \(j\),再枚举 \(j_1,j_2\) 使得 \(j_1\&j_2=j\))。
2.2. 正变换
理解方式同上,就不再阐述。
2.3. 逆变换
证明方式同上,就不再阐述。
2.4. 代码实现
void AND(int *f,int opt=1) {
for(int l=2;l<=n;l<<=1)
for(int o=0,p=l>>1;o<n;o+=l)
for(int i=o;i<o+p;++i)
f[i] = inc(f[i],f[i+p]*opt);
}
3. 按位异或卷积
3.1. 定义
定义 \(i\circledast j\) 为 \(i\& j\) 在二进制下 \(1\) 的个数的奇偶性。
定义
在证明 \(\mathtt{FWT}(A\oplus B)=\mathtt{FWT}(A)\times \mathtt{FWT}(B)\) 之前,首先有结论:\((i\circledast j)\oplus (i\circledast k)=i\circledast (j\oplus k)\).
具体证明可以对 \(i,j,k\) 二进制的每一位考虑,如果每一位都成立那么总结论也可以推知。只证明一位的话分类讨论或者打表都行 /ww.
那么
3.2. 正变换
证明就是暴力推柿子(这里只考虑 \(i<2^{n-1}\) 的情况,后半部分同理):
3.3. 逆变换
若 \(n>0\):
需要证明 \(\mathtt{FWT}(A_0+A_1)=\mathtt{FWT}(A_0)+\mathtt{FWT}(A_1)\),这个由 \(\mathtt{FWT}(A)[i]=\sum (-1)^{i\circledast j}\cdot a_j\) 可以轻松得出。
接下来就与 1.3. 逆变换
相同,直接代入即可。
3.4. 代码实现
void XOR(int *f,int opt=1) {
for(int l=2;l<=n;l<<=1)
for(int o=0,p=l>>1;o<n;o+=l)
for(int i=o;i<o+p;++i) {
int x=f[i],y=f[i+p];
f[i] = inc(x,y);
f[i+p] = dec(x,y);
if(opt==-1) f[i]*=inv_2, f[i+p]*=inv_2;
}
}
4. 高维前/后缀和与各类卷积的联系
4.1. 按位或卷积
其实就是将所有下标看作长度为 \(n\) 的二进制串。比如 \(i=(0010)_2\) 对应数组编号 \([0][0][1][0]\). 令 \(s_i,t_i\) 分别为 \(a_i,b_i\) 的前缀和。为了更清楚地说明,令 \(i=(p_1p_2\dots p_n)_2\),那么可以这样表示:
可以发现,所谓的 \(\mathtt{FWT}(A)[i]\) 其实就是 \(s_i\). 那么我们将 \(s_i\) 与 \(t_i\) 相乘,看看得到了什么:
\(\bf FBI\ Warning\):以下内容全是辣鸡博主在乱扯,如果扯得不对请在评论区指出,别点踩嗄 /ww
定义 \(f(i)=(\max(0,p_1-1)\max(0,p_2-1)\dots \max(0,p_n-1))_2\). 是的,你没有看错,这个玩意儿就是零,我也觉得好它吗怪。那么
怎么说呢,这个柿子可以用多重 for
循环来理解。如果只考虑 \(1\) 维前缀和(二进制只有 \(1\) 位)就可以画一个矩形来表示 \(s_it_i\),那么 \(s_it_i-s_{f(i)}t_{f(i)}=s_it_i-s_{i-1}t_{i-1}\),其实这个差可以看作旋转 \(180^\circ\) 的 L
形图案。
这个图案就是我们要求的 \((A|B)[i]\) 了。所以说,高维前缀和的差分就是 \(\mathtt{IFWT}\).
4.2. 按位与卷积
其实是一样的,只不过是前缀和变成了后缀和,\(\max\) 卷积变成了 \(\min\) 卷积。
4.3. \(\rm more?\)
我也只会口胡,你可以发现 \(\gcd\) 和 \(\text{lcm}\) 卷积实际上是幂次上的 \(\min\) 和 \(\max\) 卷积。
5. 题目小结
例 1.
\(\text{CF662C Binary Table}\)
考虑到 \(n\) 是很小的,可以枚举行的翻转状态,定义状态 \(i\) 得到的最小 \(1\) 个数为 \(C_i\). 设 \(A_i\) 为列状态为 \(i\) 的列数,\(B_i\) 为 \(i\) 和 \(2^n\oplus i\) 更少的 \(1\) 的个数(枚举列的翻转)。那么有:\(C_i=\sum A_jB_{i\oplus j}\). 这就是一个很板的 \(\mathtt{FWT}\) 了。但一定要注意 数据范围的计算。
例 2.
\(\text{BZOJ - 4589 Hard Nim}\)
令 \(P(x)=[x\text{ is prime}\wedge x\le m ]\),那么答案显然是 \(P^n[0]\),定义 "乘法" 为 \(\oplus\).
这道题目其实就是将 \(\mathtt{FWT}(A\oplus B)=\mathtt{FWT}(A)\times \mathtt{FWT}(B)\) 拓展到
类似于 \(A\oplus B=\mathtt{IFWT}(\mathtt{FWT}(A)\times \mathtt{FWT}(B))\),再乘上 \(C\) 得:
结论可以归纳证明。
例 3.
\(\text{51nod - 1773 }\)A 国的贸易
设 \(f_{t,i}\) 为第 \(t\) 天第 \(i\) 个城市的货物存储量。那么有:
不妨令 \(A_0=1,A_i=[\exist 2^k=i]\). 这就是异或卷积了。