FWT 学习笔记

思想

核心思想类似于 FFT,就是将原序列转化形式,可以快速计算,然后再转化为原序列。

在OI中的应用

一般是要求在 \(O(n \log n)\) 的时间求出 序列 \(c_i=\sum\limits_{j~opt~k=i}a_jb_k\),其中 \(opt\) 是一种位运算。

性质

\(\text{fwt}\) 是一种线性变换,线性变化还存在一种同行就是 \(f(a+b)=f(a)+f(b)\),所以 \(fwt(a+b)=fwt(a)+fwt(b)\)

构造 \(fwt[a]=\sum\limits_{j|i=i} a_j\)

考虑怎么将 \(a \to fwt[a]\)

\(a_0,a_1\) 分别表示最高位是 \(0,1\) 的两部分序列,则有 \(fwt[a]=merge(fwt[a_0],fwt[a_0]+fwt[a_1])\)。感性理解,最高位是 \(0\) 的部分显然不可能从最高位是 \(1\) 的部分得到新的贡献,最高位是 \(1\) 的会从除去最高位剩下的位相等得到新的贡献。所以直接分治,就可以在 \(O(n\log n)\) 的时间内求出。

\[\begin{align*} fwt[c]&=\sum\limits_{j|k|i=i} a_jb_k\\ &=\sum\limits_{j|i=i,k|i=i} a_jb_k\\ &=fwt[a] fwt[b] \end{align*} \]

这样可以在 \(O(n)\) 的时间求出 \(fwt[c]\)

现在考虑如何将 \(fwt[c]\) 还原成 \(c\)。显然之前的构造过程到过做就可以了。

构造 \(fwt[a]=\sum\limits_{j \land i=i} a_j\)

考虑如何将 \(a \to fwt[a]\)

\(a_0,a_1\) 分别表示最高位是 \(0,1\) 的两部分,则有 \(fwt[a]=merge(fwt[a_0]+fwt[a_1],fwt[a_1])\)

\[\begin{align*} fwt[c]&=\sum\limits_{j \land k \land i=i} a_j b_k\\ &=\sum\limits_{j \land i=i,k \land i=i} a_j b_k\\ &=fwt[a] fwt[b] \end{align*} \]

还原参考构造。

异或

定义 \(x*y=\text {popcount}(x \land y) \land 1\)。一个显然的性质是 \((x*y)\bigoplus(x*z)=x*(y \bigoplus z)\),感性理解一下,等号左边等价与 \(((x*y)+(x*z))\mod 2\),所以 \(y,z\) 相同的位是没有贡献的。

构造 \(fwt[i]=\sum\limits_{i*j=0}a_j-\sum\limits_{i*j=1}a_j\)

\[\begin{align*} fwt[a]*fwt[b]&=(\sum\limits_{i*j=0} a_j-\sum\limits_{i*k=1} a_k)(\sum\limits_{i*j=0} b_j-\sum\limits_{i*k=1} b_k)\\ &=\sum\limits_{i*j=0} a_jb_j+\sum\limits_{i*j=1} a_jb_j-\sum\limits_{i*j=0,i*k=1} (a_jb_k+a_kb_j)\\ &=\sum\limits_{(i*j)\oplus(i*k)=0} a_jb_k-\sum\limits_{(i*j)\oplus(i*k)=1} a_jb_k\\ &=\sum\limits_{i*(j \oplus k)=0} a_jb_k-\sum\limits_{i*(j\oplus k)=1} a_jb_k\\ &=fwt[c] \end{align*} \]

考虑将 \(a \to fwt[a]\)

\(a_0,a_1\) 分别表示最高位是 \(0,1\) 的两部分,则有 \(fwt[a]=merge(fwt[a_0]+fwt[a_1],fwt[a_0]-fwt[a_1])\),感性理解,最高位是 \(0\) 的与上最高位是 \(0,1\) 的等价与只考虑除去最高位以外的,所以对于 \(a_0\) 的部分是 \(fwt[a_0]+fwt[a_1]\),对于最高位是 \(1\) 的,与最高位是 \(0\) 的直接加上,但是对于最高位是 \(1\) 的话就要取反,所以是 \(fwt[a_1]=fwt[a_0]-fwt[a_1]\)

例题

CF914G

\[ans =\sum\limits_{p \in 2^m}\sum\limits_{(i \land j \land k)\oplus p=0} f(i)f(j)f(k) \sum\limits_{a \lor b=i,a \land b=0}1\sum\limits_{d \oplus e=k}1 \]

对于 \(B(k)=\sum\limits_{d\bigoplus e=k}1\) 显然可以 \(\text{fwtxor}\) 求出来,对于 \(A(i)=\sum\limits_{a \lor b=i,a \land b=0}1\) 枚举子集直接做,好像有 \(\log^2n\) 的卷积做法,但是我不太会。

将原式进一步转化

\[ans=\sum\limits_{p\in 2^m} \sum f(i)A(i)\sum f(k)B(k)f(p \oplus (i \land k)) \]

现在 \(\land~\text{fwtand}\) 一下,然后枚举 \(2^m\) 统计答案就行了。

时间复杂度的瓶颈在枚举子集 \(3^{17}\)

uoj#310

显然原题等价于求 \(\sum\limits_{i=1}^n2^i\sum\limits_{\text{popcount}(S)=i}a_{b_1}\oplus \cdots \oplus a_{b_i}=0\)

考虑暴力 DP,\(f_i,g_i\) 分别表示第 \(k\) 个数之前之后的异或和为 \(i\) 的个数。那么有转移 \(f_i+=2\sum\limits_{j\oplus a_k=i}g_j\),这个式子很 \(\text{fwt}\),将第 \(k\) 个数弄成多项式 \(b\),其中 \(b_0=1,b_{a_k}=2\)。转移式子就变成了 \(f_i=\sum\limits_{j\oplus k=i}g_jb_k\),但是卷 \(n\) 次很亏,因为每个多项式只有两位有值,所以考虑如何巧妙快速更新 \(fwt\)

考虑每一次的 \(b\) 的每一位只会是 \(3,-1\),所以我们知道每一位是由多少个 \(3,-1\) 乘起来。显然有 \(cnt1+cnt3=n\),所以考虑求出每一位 \(3\) 的个数,由于 \(fwt\) 是线性变换,那么显然就可以求出。

posted @ 2022-12-01 21:58  starrylasky  阅读(24)  评论(0)    收藏  举报