快速沃尔什变换&&ABC396G
FWT
快速沃尔什变换(FWT)是解决位运算卷积问题,即给定序列 \(a,b\),求序列 \(c\) 满足\(c_i=\sum_{i=j\oplus k}A_j\times B_k\),其中 \(\oplus\) 为一种位运算符号。
因为本文是讲题的前置,所以这里只讲异或卷积,也是最难的一个。
沃尔什变换其核心思想是先对序列 \(a,b\) 做一遍正变换,假设对其做正变换后得到的序列为 \(A\) 和 \(B\),然后对两个得到的新序列进行一定操作得到 \(C\),然后对 \(C\) 做一遍逆变换即可。
规定 \(popcnt(x)\) 为 \(x\) 二进制下 \(1\) 的个数,\(x\circ y=popcnt(x \ \mathrm{and }\ y)\bmod 2\) 即 \(x\) 和 \(y\) 按位与后二进制中 \(1\) 个数的奇偶性。
存在分配律 \((x\circ y)\oplus (x\circ z)=x\circ(y\oplus z)\)。
接下来是一个构造,我们定义 \(A_i=\sum_{i\circ j=0}a_j-\sum_{i\circ j=1} a_j,B_i=\sum_{i\circ j=0}b_j-\sum_{i\circ j=1} b_j,C_i=\sum_{i\circ j=0}c_j-\sum_{i\circ j=1} c_j\)。
考虑证明 \(C_i=A_i\times B_i\),笔者数学水平讲不出来,直接看一下吧[1]。
如何计算?
记 \(\mathrm{merge}(a,b)\) 表示两个序列拼接组成的序列,定义序列的加法 \(a+b\) 为对应点值相加。
对于序列 \(A\) 有 $A=\mathrm{merge}(A_0+A_1,A_0-A_1) $,不会证明。
逆变换为 \(a=\mathrm{merge}(\frac{a_0+a_1}{2},\frac{a_0-a_1}{2})\),这里异或卷积知道正变换后很容易推出逆变换。
代码实现类似FFT,不用递归用迭代。
inline void FWT(int *P,int type){
for(int i=1;i<n;i<<=1){
int p=i<<1;
for(int j=0;j<n;j+=p){
for(int k=0;k<i;k++){
FWT[j+k]=FWT[j+k]+FWT[i+j+k];
FWT[i+j+k]=FWT[j+k]-FWT[i+j+k];
if(type==-1){
FWT[j+k]>>=1;
FWT[i+j+k]>>=1;
}
}
}
}
return;
}
FWT(a,1);
FWT(b,1);
for(int i=1;i<=n;i++)a[i]*=b[i];
FWT(a,-1);
ABC396G
题意:有一个 \(h\) 行 \(w\) 列的表格,每个元素都是 \(0/1\) ,每次操作可以选择一行或一列,把 \(0/1\) 翻转,即把 \(0\) 换为 \(1\) ,把 \(1\) 换为 \(0\) 。请问经过若干次操作后,表格中最少有多少个 \(1\) 。
注意到 \(h\) 很大 \(w\) 很小,我们对行状压。
当列的翻转状态确定的时候,答案是唯一的。
设列的翻转状态为 \(x\),每行的状态为 \(y_i\),对 \(x\) 进行枚举,显然第 \(i\) 行翻转完的状态为 \(y_i\oplus x\),记作 \(s_i\)。
设一个状态中 \(0\) 的个数和 \(1\) 的个数中较小的值为 \(f\),即 \(f_s=\min{(cnt_0(s),cnt_1(s))}\)。
暴力做法为 \(ans=\min\sum_{i=1}^{h}f_{s_i}\),时间复杂度为 \(O(h\times 2^w)\),T飞。
\(ans_X=\sum_{i=1}^{h}f_{s_i}=\sum_{i=1}^{h}f_{y_i\oplus X}\)
设所有行中有 \(g_s\) 行状态为 \(s\),上式
FWT秒了。
预处理 \(f,g\) 的代码如下
for(int i=0;i<(1<<w);++i)
f[i]=f[i>>1]+(i&1);
for(int i=0;i<(1<<w);++i)
f[i]=min(f[i],w-f[i]);
for(int j=1;j<=h;j++){
long long tmp=0;
for(int i=1;i<=w;i++)
tmp+=(a[i][j]<<(i-1));
g[tmp]++;
}
设 \(2^w\) 为 \(N\),时间复杂度为 \(O(N \log N)\)