快速沃尔什变换(FWT)

4 篇文章 0 订阅
4 篇文章 0 订阅

1前言

在之前学完了FFT稍微码了一些题、也学习了一下NTT相关的知识之后,我觉得有必要学习一下FWT,这篇博客就是阐述我对FWT的理解的

2介绍

2.1解决的问题

对于FFT,它的过程本质上是 c n = ∑ i + j = n a i ∗ b j c_n=\sum_{i+j=n}a_i*b_j cn=i+j=naibj
然后考虑一下那个 i + j = n i+j=n i+j=n的情况,如果换个符号,比如 i − j = n i-j=n ij=n,那么想必你也会做,只要把B翻转就好了
在NTT的题中,其实 i ∗ j = n ( m o d p ) i*j=n\pmod p ij=n(modp),在p有原根的时候也可以做,直接把i位置的数换到 log ⁡ 原 根 i \log_{原根}i logi,然后再做NTT,最后弄回来就好了(update:对怎么样的数有原根、对原根的性质有不理解的可以看我的博客原根哦)
那么对于 i ∣ j = n i|j=n ij=n i & j = n i\& j=n i&j=n i ⊕ j = n i\oplus j=n ij=n的情况怎么办?
可以用FWT来解决
这篇文章中用到的只是快速沃尔什变换(FWT)的一个特殊情况,有兴趣可以去网上搜一搜广义的快速沃尔什是干什么的

2.2文章中有可能用到的符号&特殊说明

首先,这篇博客中提到的n(多项式的项数)如无特殊说明都是2的幂次,方便处理,如果不为2的幂次,可以在高次加0系数
然后,对于一些公式,你可能会觉得比较长,其实只是因为我列的项数比较多导致的,你可以只看第一项来看懂我在推什么


对于一个一般的多项式 f ( x ) = a 0 x 0 + a 1 x 1 + ⋅ ⋅ ⋅ + a n − 1 x n − 1 f(x)=a_0x^0+a_1x^1+···+a_{n-1}x^{n-1} f(x)=a0x0+a1x1++an1xn1
将其表示为 ( a 0 , a 1 , a 2 ⋅ ⋅ ⋅ a n − 1 ) (a_0,a_1,a_2···a_{n-1}) (a0,a1,a2an1)


定义多项式加法
C ( x ) = A ( x ) + B ( x ) = ( a 0 , a 1 , a 2 ⋅ ⋅ ⋅ a n − 1 ) + ( b 0 , b 1 , b 2 ⋅ ⋅ ⋅ b n − 1 ) = ( a 0 + b 0 , a 1 + b 1 , a 2 + b 2 ⋅ ⋅ ⋅ a n − 1 + b n − 1 ) \begin{aligned} C(x)&=A(x)+B(x)\\ &=(a_0,a_1,a_2···a_{n-1})+(b_0,b_1,b_2···b_{n-1})\\ &=(a_0+b_0,a_1+b_1,a_2+b_2···a_{n-1}+b_{n-1})\\ \end{aligned} C(x)=A(x)+B(x)=(a0,a1,a2an1)+(b0,b1,b2bn1)=(a0+b0,a1+b1,a2+b2an1+bn1)


多项式的减法把符号变成减号
C ( x ) = A ( x ) − B ( x ) = ( a 0 , a 1 , a 2 ⋅ ⋅ ⋅ a n − 1 ) − ( b 0 , b 1 , b 2 ⋅ ⋅ ⋅ b n − 1 ) = ( a 0 − b 0 , a 1 − b 1 , a 2 − b 2 ⋅ ⋅ ⋅ a n − 1 − b n − 1 ) \begin{aligned} C(x)&=A(x)-B(x)\\ &=(a_0,a_1,a_2···a_{n-1})-(b_0,b_1,b_2···b_{n-1})\\ &=(a_0-b_0,a_1-b_1,a_2-b_2···a_{n-1}-b_{n-1})\\ \end{aligned} C(x)=A(x)B(x)=(a0,a1,a2an1)(b0,b1,b2bn1)=(a0b0,a1b1,a2b2an1bn1)


多项式的对应系数相乘乘法(区别于那个FFT做的卷积的乘法
C ( x ) = A ( x ) ∗ B ( x ) = ( a 0 , a 1 , a 2 ⋅ ⋅ ⋅ a n − 1 ) ∗ ( b 0 , b 1 , b 2 ⋅ ⋅ ⋅ b n − 1 ) = ( a 0 ∗ b 0 , a 1 ∗ b 1 , a 2 ∗ b 2 ⋅ ⋅ ⋅ a n − 1 ∗ b n − 1 ) \begin{aligned} C(x)&=A(x)*B(x)\\ &=(a_0,a_1,a_2···a_{n-1})*(b_0,b_1,b_2···b_{n-1})\\ &=(a_0*b_0,a_1*b_1,a_2*b_2···a_{n-1}*b_{n-1})\\ \end{aligned} C(x)=A(x)B(x)=(a0,a1,a2an1)(b0,b1,b2bn1)=(a0b0,a1b1,a2b2an1bn1)


对于多项式的一个操作 @ ( @ ∈ { ∣ ( 或 ) , & ( 与 ) , ⊕ ( 异 或 ) } ) @(@ \in \left\{ |(或),\&(与),\oplus(异或) \right\}) @(@{(),&(),()}),请看清楚,这里的是卷积,FFT做的多项式乘法相当于这里的符号用“+”,然而多项式的“+”已经被定义过了,前面的系数相乘乘法事实上不应该用那个符号,所以没有列出
C ( x ) = A ( x ) @ B ( x ) = ( a 0 , a 1 , a 2 ⋅ ⋅ ⋅ a n − 1 ) @ ( b 0 , b 1 , b 2 ⋅ ⋅ ⋅ b n − 1 ) = ( ∑ i @ j = 0 a i ∗ b j , ∑ i @ j = 1 a i ∗ b j , ∑ i @ j = 2 a i ∗ b j ⋅ ⋅ ⋅ ∑ i @ j = n − 1 a i ∗ b j ) \begin{aligned} C(x)&=A(x)@B(x)\\ &=(a_0,a_1,a_2···a_{n-1})@(b_0,b_1,b_2···b_{n-1})\\ &=(\sum _{i@j=0}a_i*b_j,\sum _{i@j=1}a_i*b_j,\sum _{i@j=2}a_i*b_j···\sum _{i@j=n-1}a_i*b_j)\\ \end{aligned} C(x)=A(x)@B(x)=(a0,a1,a2an1)@(b0,b1,b2bn1)=(i@j=0aibj,i@j=1aibj,i@j=2aibji@j=n1aibj)
特殊的,这里的 @ @ @运算具有分配律,即 ( A + B ) @ C = A @ C + B @ C (A+B)@C=A@C+B@C (A+B)@C=A@C+B@C,至于证明也非常方便:
∑ i @ j = x ( a i + b i ) ∗ c j = ∑ i @ j = x ( a i ∗ c j + b i ∗ c j ) = ∑ i @ j = x a i ∗ c j + ∑ i @ j = x b i ∗ c j \begin{aligned} \sum _{i@j=x}(a_i+b_i)*c_j&=\sum _{i@j=x}(a_i*c_j+b_i*c_j)\\ &=\sum _{i@j=x}a_i*c_j+\sum _{i@j=x}b_i*c_j \end{aligned} i@j=x(ai+bi)cj=i@j=x(aicj+bicj)=i@j=xaicj+i@j=xbicj


另外对于一个多项式 A A A,设 n = 2 k n=2^k n=2k,定义 A 0 A_0 A0 A A A的前 2 k − 1 2^{k-1} 2k1个系数、定义 A 1 A_1 A1 A A A的后 2 k − 1 2^{k-1} 2k1个系数
定义运算 A = ( B , C ) A=(B,C) A=(B,C)表示B这个多项式后面接上C等于A
用字母表示大概意思是:
( A , B ) = ( ( a 0 , a 1 , a 2 ⋅ ⋅ ⋅ a x − 1 ) , ( b 0 , b 1 , b 2 ⋅ ⋅ ⋅ b y − 1 ) ) = ( a 0 , a 1 , a 2 ⋅ ⋅ ⋅ a x − 1 , b 0 , b 1 , b 2 ⋅ ⋅ ⋅ b y − 1 ) \begin{aligned} (A,B)&=((a_0,a_1,a_2···a_{x-1}),(b_0,b_1,b_2···b_{y-1}))\\ &=(a_0,a_1,a_2···a_{x-1},b_0,b_1,b_2···b_{y-1})\\ \end{aligned} (A,B)=((a0,a1,a2ax1),(b0,b1,b2by1))=(a0,a1,a2ax1,b0,b1,b2by1)

2.3FWT的大致思路

假设我们现在有两个多项式 A A A B B B以及一个位运算符 @ @ @,思路和FFT相似,先求出某个多项式 F W T ( A ) FWT(A) FWT(A) F W T ( B ) FWT(B) FWT(B),然后对应相乘得到多项式 F W T ( C ) FWT(C) FWT(C),最后进行 I F W T IFWT IFWT,得出结果 C C C

3实现

update:这里的前两个(and/or)更严格意义上来说是FMT,本篇博客提供FMT本身的相关内容,另一篇博客提供FMT的更多小应用

3.1or运算(本质FMT)
3.1.1构造

先来讲or运算吧
现在要求的是: c n = ∑ i ∣ j = n a i b j c_n=\sum_{i|j=n}a_ib_j cn=ij=naibj
定义FWT(A):
F W T ( A ) = ( ∑ i ∣ 0 = 0 a i , ∑ i ∣ 1 = 1 a i , ∑ i ∣ 2 = 2 a i ⋅ ⋅ ⋅ ∑ i ∣ ( n − 1 ) = ( n − 1 ) a i ) \begin{aligned} FWT(A)=(\sum_{i|0=0}a_i,\sum_{i|1=1}a_i,\sum_{i|2=2}a_i···\sum_{i|(n-1)=(n-1)}a_i) \end{aligned} FWT(A)=(i0=0ai,i1=1ai,i2=2aii(n1)=(n1)ai)
容易发现一件事: F W T ( A ∣ B ) = F W T ( A ) ∗ F W T ( B ) FWT(A|B)=FWT(A)*FWT(B) FWT(AB)=FWT(A)FWT(B)
证明:
F W T ( A ) ∗ F W T ( B ) = ( ∑ i ∣ 0 = 0 a i , ∑ i ∣ 1 = 1 a i , ∑ i ∣ 2 = 2 a i ⋅ ⋅ ⋅ ∑ i ∣ ( n − 1 ) = ( n − 1 ) a i ) ∗ ( ∑ i ∣ 0 = 0 b i , ∑ i ∣ 1 = 1 b i , ∑ i ∣ 2 = 2 b i ⋅ ⋅ ⋅ ∑ i ∣ ( n − 1 ) = ( n − 1 ) b i ) = ( ( ∑ i ∣ 0 = 0 a i ) ∗ ( ∑ j ∣ 0 = 0 b j ) , ( ∑ i ∣ 1 = 1 a i ) ∗ ( ∑ j ∣ 1 = 1 b j ) , ( ∑ i ∣ 2 = 2 a i ) ∗ ( ∑ j ∣ 2 = 2 b j ) ⋅ ⋅ ⋅ ( ∑ i ∣ ( n − 1 ) = ( n − 1 ) a i ) ∗ ( ∑ j ∣ ( n − 1 ) = ( n − 1 ) b j ) ) = ( ∑ i ∣ j ∣ 0 = 0 a i ∗ b j , ∑ i ∣ j ∣ 1 = 1 a i ∗ b j , ∑ i ∣ j ∣ 2 = 2 a i ∗ b j ⋅ ⋅ ⋅ ∑ i ∣ j ∣ ( n − 1 ) = ( n − 1 ) a i ∗ b j ) = ( ∑ k ∣ 0 = 0 ∑ i ∣ j = k a i ∗ b j , ∑ k ∣ 1 = 1 ∑ i ∣ j = k a i ∗ b j , ∑ k ∣ 2 = 2 ∑ i ∣ j = k a i ∗ b j ⋅ ⋅ ⋅ ∑ k ∣ ( n − 1 ) = ( n − 1 ) ∑ i ∣ j = k a i ∗ b j ) = F W T ( A ∣ B ) \begin{aligned} FWT(A)*FWT(B)&=(\sum_{i|0=0}a_i,\sum_{i|1=1}a_i,\sum_{i|2=2}a_i···\sum_{i|(n-1)=(n-1)}a_i)*(\sum_{i|0=0}b_i,\sum_{i|1=1}b_i,\sum_{i|2=2}b_i···\sum_{i|(n-1)=(n-1)}b_i)\\ &=((\sum_{i|0=0}a_i)*(\sum_{j|0=0}b_j),(\sum_{i|1=1}a_i)*(\sum_{j|1=1}b_j),(\sum_{i|2=2}a_i)*(\sum_{j|2=2}b_j)···(\sum_{i|(n-1)=(n-1)}a_i)*(\sum_{j|(n-1)=(n-1)}b_j))\\ &=(\sum_{i|j|0=0}a_i*b_j,\sum_{i|j|1=1}a_i*b_j,\sum_{i|j|2=2}a_i*b_j···\sum_{i|j|(n-1)=(n-1)}a_i*b_j)\\ &=(\sum_{k|0=0}\sum_{i|j=k}a_i*b_j,\sum_{k|1=1}\sum_{i|j=k}a_i*b_j,\sum_{k|2=2}\sum_{i|j=k}a_i*b_j···\sum_{k|(n-1)=(n-1)}\sum_{i|j=k}a_i*b_j)\\ &=FWT(A|B) \end{aligned} FWT(A)FWT(B)=(i0=0ai,i1=1ai,i2=2aii(n1)=(n1)ai)(i0=0bi,i1=1bi,i2=2bii(n1)=(n1)bi)=((i0=0ai)(j0=0bj),(i1=1ai)(j1=1bj),(i2=2ai)(j2=2bj)(i(n1)=(n1)ai)(j(n1)=(n1)bj))=(ij0=0aibj,ij1=1aibj,ij2=2aibjij(n1)=(n1)aibj)=(k0=0ij=kaibj,k1=1ij=kaibj,k2=2ij=kaibjk(n1)=(n1)ij=kaibj)=FWT(AB)
当然这个的证明还有其它的很多方法,我觉得我的这个证明还是比较清楚的
也就是说,现在:

  • 问题一:
    已知一个多项式A,求FWT(A)
    即FWT
  • 问题二:
    已知FWT(A),求A
    即IFWT

解决这两个问题,我们就能够:

  • 已知A,B
  • FWT,求出FWT(A),FWT(B)
  • 对应系数相乘,求出FWT(A|B)
  • IFWT,求出A|B
3.1.2计算

我们可以递归定义
F W T ( A ) = { ( F W T ( A 0 ) , F W T ( A 0 + A 1 ) ) ( n ≠ 1 ) A ( n = 1 ) FWT(A)=\begin{cases} (FWT(A_0),FWT(A_0+A_1))(n\ne1)\\ A(n=1) \end{cases} FWT(A)={(FWT(A0),FWT(A0+A1))(n̸=1)A(n=1)
这么定义的原因:因为 A 0 A_0 A0的编号的最高位都是0, A 1 A_1 A1的编号与 A 0 A_0 A0的编号的最高位变成1的结果一一对应,因为是or运算,所以 A 0 A_0 A0能到 A 1 A_1 A1里一一对应的贡献
然后考虑 I F W T IFWT IFWT(定义 I F W T ( F W T ( A ) ) = A IFWT(FWT(A))=A IFWT(FWT(A))=A),由于 F W T ( A + B ) = F W T ( A ) + F W T ( B ) FWT(A+B)=FWT(A)+FWT(B) FWT(A+B)=FWT(A)+FWT(B)(这个证明不用说了吧,看我的定义,这个相当于乘法分配律拆一下就好了),可以列出 I F W T IFWT IFWT的式子,推导:
已知 F W T ( A ) 0 FWT(A)_0 FWT(A)0 F W T ( A ) 1 FWT(A)_1 FWT(A)1,求 A 0 A_0 A0 A 1 A_1 A1
∵ F W T ( A ) 0 = F W T ( A 0 ) ∴ A 0 = I D F T ( F W T ( A 0 ) ) = I D F T ( F W T ( A ) 0 ) ∵ F W T ( A ) 1 = F W T ( A 0 ) + F W T ( A 1 ) ∴ A 1 = I D F T ( F W T ( A 1 ) ) = I D F T ( F W T ( A ) 1 − F W T ( A ) 0 ) \because FWT(A)_0=FWT(A_0)\\ \therefore A_0=IDFT(FWT(A_0))=IDFT(FWT(A)_0)\\ \because FWT(A)_1=FWT(A_0)+FWT(A_1)\\ \therefore A_1= IDFT(FWT(A_1))=IDFT(FWT(A)_1-FWT(A)_0) FWT(A)0=FWT(A0)A0=IDFT(FWT(A0))=IDFT(FWT(A)0)FWT(A)1=FWT(A0)+FWT(A1)A1=IDFT(FWT(A1))=IDFT(FWT(A)1FWT(A)0)


总结:
I F W T ( A ) = { ( I F W T ( A 0 ) , I F W T ( A 1 − A 0 ) ) ( n ≠ 1 ) A ( n = 1 ) IFWT(A)=\begin{cases} (IFWT(A_0),IFWT(A_1-A_0))(n\ne1)\\ A(n=1) \end{cases} IFWT(A)={(IFWT(A0),IFWT(A1A0))(n̸=1)A(n=1)
然后就可以写代码了(代码和FFT有点像)

inline void FWT(LL*A,const int fla)
{
    for(rg int i=1;i<lenth;i<<=1)
        for(rg int j=0;j<lenth;j+=(i<<1))
            for(rg int k=0;k<i;k++)
                A[j+k+i]+=A[j+k]*fla;
}

然后or运算的FWT就没了

3.2and运算(本质FMT)
3.2.1构造

and其实和or差的不多
现在要求的是: c n = ∑ i &amp; j = n a i b j c_n=\sum_{i\&amp;j=n}a_ib_j cn=i&j=naibj
同样定义FWT(A):
F W T ( A ) = ( ∑ i &amp; 0 = 0 a i , ∑ i &amp; 1 = 1 a i , ∑ i &amp; 2 = 2 a i ⋅ ⋅ ⋅ ∑ i &amp; ( n − 1 ) = ( n − 1 ) a i ) \begin{aligned} FWT(A)=(\sum_{i\&amp;0=0}a_i,\sum_{i\&amp;1=1}a_i,\sum_{i\&amp;2=2}a_i···\sum_{i\&amp;(n-1)=(n-1)}a_i) \end{aligned} FWT(A)=(i&0=0ai,i&1=1ai,i&2=2aii&(n1)=(n1)ai)
同样的: F W T ( A &amp; B ) = F W T ( A ) ∗ F W T ( B ) FWT(A\&amp;B)=FWT(A)*FWT(B) FWT(A&B)=FWT(A)FWT(B)
证明:
F W T ( A ) ∗ F W T ( B ) = ( ∑ i &amp; 0 = 0 a i , ∑ i &amp; 1 = 1 a i , ∑ i &amp; 2 = 2 a i ⋅ ⋅ ⋅ ∑ i &amp; ( n − 1 ) = ( n − 1 ) a i ) ∗ ( ∑ i &amp; 0 = 0 b i , ∑ i &amp; 1 = 1 b i , ∑ i &amp; 2 = 2 b i ⋅ ⋅ ⋅ ∑ i &amp; ( n − 1 ) = ( n − 1 ) b i ) = ( ( ∑ i &amp; 0 = 0 a i ) ∗ ( ∑ j &amp; 0 = 0 b j ) , ( ∑ i &amp; 1 = 1 a i ) ∗ ( ∑ j &amp; 1 = 1 b j ) , ( ∑ i &amp; 2 = 2 a i ) ∗ ( ∑ j &amp; 2 = 2 b j ) ⋅ ⋅ ⋅ ( ∑ i &amp; ( n − 1 ) = ( n − 1 ) a i ) ∗ ( ∑ j &amp; ( n − 1 ) = ( n − 1 ) b j ) ) = ( ∑ i &amp; j &amp; 0 = 0 a i ∗ b j , ∑ i &amp; j &amp; 1 = 1 a i ∗ b j , ∑ i &amp; j &amp; 2 = 2 a i ∗ b j ⋅ ⋅ ⋅ ∑ i &amp; j &amp; ( n − 1 ) = ( n − 1 ) a i ∗ b j ) = ( ∑ k &amp; 0 = 0 ∑ i &amp; j = k a i ∗ b j , ∑ k &amp; 1 = 1 ∑ i &amp; j = k a i ∗ b j , ∑ k &amp; 2 = 2 ∑ i &amp; j = k a i ∗ b j ⋅ ⋅ ⋅ ∑ k &amp; ( n − 1 ) = ( n − 1 ) ∑ i &amp; j = k a i ∗ b j ) = F W T ( A &amp; B ) \begin{aligned} FWT(A)*FWT(B)&amp;=(\sum_{i\&amp;0=0}a_i,\sum_{i\&amp;1=1}a_i,\sum_{i\&amp;2=2}a_i···\sum_{i\&amp;(n-1)=(n-1)}a_i)*(\sum_{i\&amp;0=0}b_i,\sum_{i\&amp;1=1}b_i,\sum_{i\&amp;2=2}b_i···\sum_{i\&amp;(n-1)=(n-1)}b_i)\\ &amp;=((\sum_{i\&amp;0=0}a_i)*(\sum_{j\&amp;0=0}b_j),(\sum_{i\&amp;1=1}a_i)*(\sum_{j\&amp;1=1}b_j),(\sum_{i\&amp;2=2}a_i)*(\sum_{j\&amp;2=2}b_j)···(\sum_{i\&amp;(n-1)=(n-1)}a_i)*(\sum_{j\&amp;(n-1)=(n-1)}b_j))\\ &amp;=(\sum_{i\&amp;j\&amp;0=0}a_i*b_j,\sum_{i\&amp;j\&amp;1=1}a_i*b_j,\sum_{i\&amp;j\&amp;2=2}a_i*b_j···\sum_{i\&amp;j\&amp;(n-1)=(n-1)}a_i*b_j)\\ &amp;=(\sum_{k\&amp;0=0}\sum_{i\&amp;j=k}a_i*b_j,\sum_{k\&amp;1=1}\sum_{i\&amp;j=k}a_i*b_j,\sum_{k\&amp;2=2}\sum_{i\&amp;j=k}a_i*b_j···\sum_{k\&amp;(n-1)=(n-1)}\sum_{i\&amp;j=k}a_i*b_j)\\ &amp;=FWT(A\&amp;B) \end{aligned} FWT(A)FWT(B)=(i&0=0ai,i&1=1ai,i&2=2aii&(n1)=(n1)ai)(i&0=0bi,i&1=1bi,i&2=2bii&(n1)=(n1)bi)=((i&0=0ai)(j&0=0bj),(i&1=1ai)(j&1=1bj),(i&2=2ai)(j&2=2bj)(i&(n1)=(n1)ai)(j&(n1)=(n1)bj))=(i&j&0=0aibj,i&j&1=1aibj,i&j&2=2aibji&j&(n1)=(n1)aibj)=(k&0=0i&j=kaibj,k&1=1i&j=kaibj,k&2=2i&j=kaibjk&(n1)=(n1)i&j=kaibj)=FWT(A&B)
然后没啦

3.2.2计算

我们一样可以递归定义
F W T ( A ) = { ( F W T ( A 0 + A 1 ) , F W T ( A 1 ) ) ( n ≠ 1 ) A ( n = 1 ) FWT(A)=\begin{cases} (FWT(A_0+A_1),FWT(A_1))(n\ne1)\\ A(n=1) \end{cases} FWT(A)={(FWT(A0+A1),FWT(A1))(n̸=1)A(n=1)
这么定义的原因(其实和or差不多):因为 A 0 A_0 A0的编号的最高位都是0, A 1 A_1 A1的编号与 A 0 A_0 A0的编号的最高位变成1的结果一一对应,因为是and运算,所以 A 1 A_1 A1能到 A 0 A_0 A0里一一对应的贡献
然后是 I F W T IFWT IFWT,推导:
已知 F W T ( A ) 0 FWT(A)_0 FWT(A)0 F W T ( A ) 1 FWT(A)_1 FWT(A)1,求 A 0 A_0 A0 A 1 A_1 A1
∵ F W T ( A ) 0 = F W T ( A 0 ) + F W T ( A 1 ) ∴ A 0 = I D F T ( F W T ( A 0 ) ) = I D F T ( F W T ( A ) 0 − F W T ( A ) 1 ) ∵ F W T ( A ) 1 = F W T ( A 1 ) ∴ A 1 = I D F T ( F W T ( A 1 ) ) = I D F T ( F W T ( A ) 1 ) \because FWT(A)_0=FWT(A_0)+FWT(A_1)\\ \therefore A_0=IDFT(FWT(A_0))=IDFT(FWT(A)_0-FWT(A)_1)\\ \because FWT(A)_1=FWT(A_1)\\ \therefore A_1= IDFT(FWT(A_1))=IDFT(FWT(A)_1) FWT(A)0=FWT(A0)+FWT(A1)A0=IDFT(FWT(A0))=IDFT(FWT(A)0FWT(A)1)FWT(A)1=FWT(A1)A1=IDFT(FWT(A1))=IDFT(FWT(A)1)


总结:
I F W T ( A ) = { ( I F W T ( A 0 − A 1 ) , I F W T ( A 1 ) ) ( n ≠ 1 ) A ( n = 1 ) IFWT(A)=\begin{cases} (IFWT(A_0-A_1),IFWT(A_1))(n\ne1)\\ A(n=1) \end{cases} IFWT(A)={(IFWT(A0A1),IFWT(A1))(n̸=1)A(n=1)
然后上代码

inline void FWT(LL*A,const int fla)
{
    for(rg int i=1;i<lenth;i<<=1)
        for(rg int j=0;j<lenth;j+=(i<<1))
            for(rg int k=0;k<i;k++)
                A[j+k]+=A[j+k+i]*fla;
}

3.3xor运算(本质FWT)
3.3.1构造

哦不,这是最烦的xor运算,我的思路在xor下是最烦的
现在要求的是: c n = ∑ i ⊕ j = n a i b j c_n=\sum_{i\oplus j=n}a_ib_j cn=ij=naibj
也要定义FWT(A),然而定义有一点烦:
update:这是新版本,比较清晰易懂:

符号说明:
p o p c o u n t ( x ) popcount(x) popcount(x)等于x在二进制下1的数量,我在下面简写为 p c ( x ) pc(x) pc(x)

F W T ( A ) = ( ∑ ( − 1 ) p c ( i &amp; 0 ) a i , ∑ ( − 1 ) p c ( i &amp; 1 ) a i ⋅ ⋅ ⋅ ∑ ( − 1 ) p c ( i &amp; ( n − 1 ) ) a i ) FWT(A)=(\sum (-1)^{pc(i\&amp;0)}a_i,\sum (-1)^{pc(i\&amp;1)}a_i···\sum (-1)^{pc(i\&amp;(n-1))}a_i) FWT(A)=((1)pc(i&0)ai,(1)pc(i&1)ai(1)pc(i&(n1))ai)
证明: F W T ( A ⊕ B ) = F W T ( A ) ∗ F W T ( B ) FWT(A\oplus B)=FWT(A)*FWT(B) FWT(AB)=FWT(A)FWT(B)
F W T ( A ) ∗ F W T ( B ) = ( ∑ ( − 1 ) p c ( i &amp; 0 ) a i , ∑ ( − 1 ) p c ( i &amp; 1 ) a i ⋅ ⋅ ⋅ ∑ ( − 1 ) p c ( i &amp; ( n − 1 ) ) a i ) ∗ ( ∑ ( − 1 ) p c ( i &amp; 0 ) b i , ∑ ( − 1 ) p c ( i &amp; 1 ) b i ⋅ ⋅ ⋅ ∑ ( − 1 ) p c ( i &amp; ( n − 1 ) ) b i ) = ( ( ∑ ( − 1 ) p c ( i &amp; 0 ) a i ) ∗ ( ( ∑ ( − 1 ) p c ( j &amp; 0 ) b j ) ) , ( ∑ ( − 1 ) p c ( i &amp; 1 ) a i ) ∗ ( ∑ ( − 1 ) p c ( j &amp; 1 ) b j ) ⋅ ⋅ ⋅ ( ∑ ( − 1 ) p c ( i &amp; ( n − 1 ) ) a i ) ∗ ( ∑ ( − 1 ) p c ( j &amp; ( n − 1 ) ) b j ) ) = ( ∑ ( − 1 ) p c ( i ⊕ j &amp; 0 ) a i ∗ b j , ( ∑ ( − 1 ) p c ( i ⊕ j &amp; 1 ) a i ∗ b j ⋅ ⋅ ⋅ ( ∑ ( − 1 ) p c ( i ⊕ j &amp; ( n − 1 ) ) a i ∗ b j ) = ( ∑ ( − 1 ) p c ( k &amp; 0 ) ∑ i ⊕ j = k a i ∗ b j , ∑ ( − 1 ) p c ( k &amp; 1 ) ∑ i ⊕ j = k a i ∗ b j ⋅ ⋅ ⋅ ∑ ( − 1 ) p c ( k &amp; ( n − 1 ) ) ∑ i ⊕ j = k a i ∗ b j ) = F W T ( A ⊕ B ) \begin{aligned} FWT(A)*FWT(B)&amp;=(\sum (-1)^{pc(i\&amp;0)}a_i,\sum (-1)^{pc(i\&amp;1)}a_i···\sum (-1)^{pc(i\&amp;(n-1))}a_i)*(\sum (-1)^{pc(i\&amp;0)}b_i,\sum (-1)^{pc(i\&amp;1)}b_i···\sum (-1)^{pc(i\&amp;(n-1))}b_i)\\ &amp;=((\sum (-1)^{pc(i\&amp;0)}a_i)*((\sum (-1)^{pc(j\&amp;0)}b_j)),(\sum (-1)^{pc(i\&amp;1)}a_i)*(\sum (-1)^{pc(j\&amp;1)}b_j)···(\sum (-1)^{pc(i\&amp;(n-1))}a_i)*(\sum (-1)^{pc(j\&amp;(n-1))}b_j))\\ &amp;=(\sum (-1)^{pc(i\oplus j\&amp;0)}a_i*b_j,(\sum (-1)^{pc(i\oplus j\&amp;1)}a_i*b_j···(\sum (-1)^{pc(i\oplus j\&amp;(n-1))}a_i*b_j)\\ &amp;=(\sum (-1)^{pc(k\&amp;0)}\sum _{i\oplus j=k}a_i*b_j,\sum (-1)^{pc(k\&amp;1)}\sum _{i\oplus j=k}a_i*b_j···\sum (-1)^{pc(k\&amp;(n-1))}\sum _{i\oplus j=k}a_i*b_j)\\ &amp;=FWT(A\oplus B) \end{aligned} FWT(A)FWT(B)=((1)pc(i&0)ai,(1)pc(i&1)ai(1)pc(i&(n1))ai)((1)pc(i&0)bi,(1)pc(i&1)bi(1)pc(i&(n1))bi)=(((1)pc(i&0)ai)(((1)pc(j&0)bj)),((1)pc(i&1)ai)((1)pc(j&1)bj)((1)pc(i&(n1))ai)((1)pc(j&(n1))bj))=((1)pc(ij&0)aibj,((1)pc(ij&1)aibj((1)pc(ij&(n1))aibj)=((1)pc(k&0)ij=kaibj,(1)pc(k&1)ij=kaibj(1)pc(k&(n1))ij=kaibj)=FWT(AB)


下面是老版本,是一个不太标准的形式,你可以直接跳到3.3.2

符号说明:
p o p c o u n t ( x ) popcount(x) popcount(x)等于x在二进制下1的数量,我在下面简写为 p c ( x ) pc(x) pc(x)
逻辑运算符a?b:c 代表若a为真,那么值为b,若为假,值为c

F W T ( A ) = ( ∑ p c ( i &amp; 0 ) m o d   2 = = 0 ? a i : − a i , ∑ p c ( i &amp; 1 ) m o d   2 = = 0 ? a i : − a i , ∑ p c ( i &amp; 2 ) m o d   2 = = 0 ? a i : − a i ⋅ ⋅ ⋅ ∑ p c ( i &amp; ( n − 1 ) ) m o d   2 = = 0 ? a i : − a i ) FWT(A)=(\sum pc(i\&amp;0)mod\ 2==0? a_i:-a_i,\sum pc(i\&amp;1)mod\ 2==0? a_i:-a_i,\sum pc(i\&amp;2)mod\ 2==0? a_i:-a_i···\sum pc(i\&amp;(n-1))mod\ 2==0? a_i:-a_i) FWT(A)=(pc(i&0)mod 2==0?ai:ai,pc(i&1)mod 2==0?ai:ai,pc(i&2)mod 2==0?ai:aipc(i&(n1))mod 2==0?ai:ai)
容易发现一件事: F W T ( A ⊕ B ) = F W T ( A ) ∗ F W T ( B ) FWT(A\oplus B)=FWT(A)*FWT(B) FWT(AB)=FWT(A)FWT(B)
证明:
F W T ( A ) ∗ F W T ( B ) = ( ∑ p c ( i &amp; 0 ) m o d   2 = = 0 ? a i : − a i , ∑ p c ( i &amp; 1 ) m o d   2 = = 0 ? a i : − a i , ∑ p c ( i &amp; 2 ) m o d   2 = = 0 ? a i : − a i ⋅ ⋅ ⋅ ∑ p c ( i &amp; ( n − 1 ) ) m o d   2 = = 0 ? a i : − a i ) ∗ ( ∑ p c ( i &amp; 0 ) m o d   2 = = 0 ? b i : − b i , ∑ p c ( i &amp; 1 ) m o d   2 = = 0 ? b i : − b i , ∑ p c ( i &amp; 2 ) m o d   2 = = 0 ? b i : − b i ⋅ ⋅ ⋅ ∑ p c ( i &amp; ( n − 1 ) ) m o d   2 = = 0 ? b i : − b i ) = ( ( ∑ p c ( i &amp; 0 ) m o d   2 = = 0 ? a i : − a i ) ∗ ( ∑ p c ( j &amp; 0 ) m o d   2 = = 0 ? b j : − b j ) , ( ∑ p c ( i &amp; 1 ) m o d   2 = = 0 ? a i : − a i ) ∗ ( ∑ p c ( j &amp; 1 ) m o d   2 = = 0 ? b j : − b j ) , ( ∑ p c ( i &amp; 2 ) m o d   2 = = 0 ? a i : − a i ) ∗ ( ∑ p c ( j &amp; 2 ) m o d   2 = = 0 ? b j : − b j ) ⋅ ⋅ ⋅ ( ∑ p c ( i &amp; ( n − 1 ) ) m o d   2 = = 0 ? a i : − a i ) ∗ ( ∑ p c ( j &amp; ( n − 1 ) ) m o d   2 = = 0 ? b j : − b j ) ) = ( ∑ p c ( ( i ⊕ j ) &amp; 0 ) m o d   2 = = 0 ? a i ∗ b j : − a i ∗ b j , ∑ p c ( ( i ⊕ j ) &amp; 1 ) m o d   2 = = 0 ? a i ∗ b j : − a i ∗ b j , ∑ p c ( ( i ⊕ j ) &amp; 2 ) m o d   2 = = 0 ? a i ∗ b j : − a i ∗ b j ⋅ ⋅ ⋅ ∑ p c ( ( i ⊕ j ) &amp; ( n − 1 ) ) m o d   2 = = 0 ? a i ∗ b j : − a i ∗ b j ) = ( ∑ p c ( k &amp; 0 ) m o d   2 = = 0 ? ∑ i ⊕ j = k a i ∗ b j : − ∑ i ⊕ j = k a i ∗ b j , ∑ p c ( k &amp; 1 ) m o d   2 = = 0 ? ∑ i ⊕ j = k a i ∗ b j : − ∑ i ⊕ j = k a i ∗ b j , ∑ p c ( k &amp; 2 ) m o d   2 = = 0 ? ∑ i ⊕ j = k a i ∗ b j : − ∑ i ⊕ j = k a i ∗ b j ⋅ ⋅ ⋅ ∑ p c ( k &amp; ( n − 1 ) ) m o d   2 = = 0 ? ∑ i ⊕ j = k a i ∗ b j : − ∑ i ⊕ j = k a i ∗ b j ) = F W T ( A ⊕ B ) \begin{aligned} FWT(A)*FWT(B)&amp;=(\sum pc(i\&amp;0)mod\ 2==0? a_i:-a_i,\sum pc(i\&amp;1)mod\ 2==0? a_i:-a_i,\sum pc(i\&amp;2)mod\ 2==0? a_i:-a_i···\sum pc(i\&amp;(n-1))mod\ 2==0? a_i:-a_i)*(\sum pc(i\&amp;0)mod\ 2==0? b_i:-b_i,\sum pc(i\&amp;1)mod\ 2==0? b_i:-b_i,\sum pc(i\&amp;2)mod\ 2==0? b_i:-b_i···\sum pc(i\&amp;(n-1))mod\ 2==0? b_i:-b_i)\\ &amp;=((\sum pc(i\&amp;0)mod\ 2==0? a_i:-a_i)*(\sum pc(j\&amp;0)mod\ 2==0? b_j:-b_j),(\sum pc(i\&amp;1)mod\ 2==0? a_i:-a_i)*(\sum pc(j\&amp;1)mod\ 2==0? b_j:-b_j),(\sum pc(i\&amp;2)mod\ 2==0? a_i:-a_i)*(\sum pc(j\&amp;2)mod\ 2==0? b_j:-b_j)···(\sum pc(i\&amp;(n-1))mod\ 2==0? a_i:-a_i)*(\sum pc(j\&amp;(n-1))mod\ 2==0? b_j:-b_j))\\ &amp;=(\sum pc((i\oplus j)\&amp;0)mod\ 2==0? a_i*b_j:-a_i*b_j,\sum pc((i\oplus j)\&amp;1)mod\ 2==0? a_i*b_j:-a_i*b_j,\sum pc((i\oplus j)\&amp;2)mod\ 2==0? a_i*b_j:-a_i*b_j···\sum pc((i\oplus j)\&amp;(n-1))mod\ 2==0? a_i*b_j:-a_i*b_j)\\ &amp;=(\sum pc(k\&amp;0)mod\ 2==0?\sum_{i \oplus j=k}a_i*b_j:-\sum_{i \oplus j=k}a_i*b_j,\sum pc(k\&amp;1)mod\ 2==0? \sum_{i \oplus j=k}a_i*b_j:-\sum_{i \oplus j=k}a_i*b_j,\sum pc(k\&amp;2)mod\ 2==0? \sum_{i \oplus j=k}a_i*b_j:-\sum_{i \oplus j=k}a_i*b_j···\sum pc(k\&amp;(n-1))mod\ 2==0? \sum_{i \oplus j=k}a_i*b_j:-\sum_{i \oplus j=k}a_i*b_j)\\ &amp;=FWT(A\oplus B) \end{aligned} FWT(A)FWT(B)=(pc(i&0)mod 2==0?ai:ai,pc(i&1)mod 2==0?ai:ai,pc(i&2)mod 2==0?ai:aipc(i&(n1))mod 2==0?ai:ai)(pc(i&0)mod 2==0?bi:bi,pc(i&1)mod 2==0?bi:bi,pc(i&2)mod 2==0?bi:bipc(i&(n1))mod 2==0?bi:bi)=((pc(i&0)mod 2==0?ai:ai)(pc(j&0)mod 2==0?bj:bj),(pc(i&1)mod 2==0?ai:ai)(pc(j&1)mod 2==0?bj:bj),(pc(i&2)mod 2==0?ai:ai)(pc(j&2)mod 2==0?bj:bj)(pc(i&(n1))mod 2==0?ai:ai)(pc(j&(n1))mod 2==0?bj:bj))=(pc((ij)&0)mod 2==0?aibj:aibj,pc((ij)&1)mod 2==0?aibj:aibj,pc((ij)&2)mod 2==0?aibj:aibjpc((ij)&(n1))mod 2==0?aibj:aibj)=(pc(k&0)mod 2==0?ij=kaibj:ij=kaibj,pc(k&1)mod 2==0?ij=kaibj:ij=kaibj,pc(k&2)mod 2==0?ij=kaibj:ij=kaibjpc(k&(n1))mod 2==0?ij=kaibj:ij=kaibj)=FWT(AB)
这个证明写的我累死了


证完就好了

3.3.2计算

定义
F W T ( A ) = { ( F W T ( A 0 + A 1 ) , F W T ( A 0 − A 1 ) ) ( n ≠ 1 ) A ( n = 1 ) FWT(A)=\begin{cases} (FWT(A_0+A_1),FWT(A_0-A_1))(n\ne1)\\ A(n=1) \end{cases} FWT(A)={(FWT(A0+A1),FWT(A0A1))(n̸=1)A(n=1)
我觉得FWT的脑洞真大,为啥这样就好了呢
就让我来解释一下吧
前提条件一样 F W T ( A + B ) = F W T ( A ) + F W T ( B ) FWT(A+B)=FWT(A)+FWT(B) FWT(A+B)=FWT(A)+FWT(B),通过这个把式子拆开
考虑当前二进制最高位,前 2 k − 1 2^{k-1} 2k1项的位置最高位都为0,所以and最高位之后一定不会是1所以 A 0 A_0 A0 A 1 A_1 A1的贡献都是原符号,由于后 2 k − 1 2^{k-1} 2k1项的位置最高位都为1,所以 A 0 A_0 A0的贡献都是原符号, A 1 A_1 A1的贡献符号反号
那么IFWT呢?
推导:
已知 F W T ( A ) 0 FWT(A)_0 FWT(A)0 F W T ( A ) 1 FWT(A)_1 FWT(A)1,求 A 0 A_0 A0 A 1 A_1 A1
∵ F W T ( A ) 0 = F W T ( A 0 ) + F W T ( A 1 ) , F W T ( A ) 1 = F W T ( A 0 ) − F W T ( A 1 ) ∴ A 0 = I D F T ( F W T ( A 0 ) ) = I D F T ( F W T ( A ) 0 + F W T ( A ) 1 2 ) , A 1 = I D F T ( F W T ( A 1 ) ) = I D F T ( F W T ( A ) 0 − F W T ( A ) 1 2 ) \because FWT(A)_0=FWT(A_0)+FWT(A_1),FWT(A)_1=FWT(A_0)-FWT(A_1)\\ \therefore A_0=IDFT(FWT(A_0))=IDFT(\frac {FWT(A)_0+FWT(A)_1}2),A_1= IDFT(FWT(A_1))=IDFT(\frac {FWT(A)_0-FWT(A)_1}2) FWT(A)0=FWT(A0)+FWT(A1)FWT(A)1=FWT(A0)FWT(A1)A0=IDFT(FWT(A0))=IDFT(2FWT(A)0+FWT(A)1)A1=IDFT(FWT(A1))=IDFT(2FWT(A)0FWT(A)1)


总结:
I F W T ( A ) = { ( I F W T ( A 0 ) + I F W T ( A 1 ) 2 , I F W T ( A 0 ) − I F W T ( A 1 ) 2 ) ( n ≠ 1 ) A ( n = 1 ) IFWT(A)=\begin{cases} (\frac {IFWT(A_0)+IFWT(A_1)}{2},\frac {IFWT(A_0)-IFWT(A_1)}{2})(n\ne1)\\ A(n=1) \end{cases} IFWT(A)={(2IFWT(A0)+IFWT(A1),2IFWT(A0)IFWT(A1))(n̸=1)A(n=1)

代码

inline void FWT(LL*A,const int fla)
{
    for(rg int i=1;i<lenth;i<<=1)
        for(rg int j=0;j<lenth;j+=(i<<1))
            for(rg int k=0;k<i;k++)
            {
                const int x=A[j+k],y=A[j+k+i];
                A[j+k]=x+y;
                A[j+k+i]=x-y;
                if(fla==-1)A[j+k]/=2,A[j+k+i]/=2;
            }
}

然后好像还有个优化,在一定条件下可以使用,就是把除法留到最后除,代码:

inline void FWT(LL*A,const int fla)
{
    for(rg int i=1;i<lenth;i<<=1)
        for(rg int j=0;j<lenth;j+=(i<<1))
            for(rg int k=0;k<i;k++)
            {
                const int x=A[j+k],y=A[j+k+i];
                A[j+k]=x+y;
                A[j+k+i]=x-y;
            }
    if(fla==-1)
        for(rg int i=0;i<lenth;i++)
            A[i]/=lenth;
}

这就是xor的FWT啦

4总结

这个FWT写了好久,终于完结了,其实FWT本身代码并不长,关键在于理解。FWT用到的地方没FFT多,但依然值得学一下,拓宽思路
相关的题?
例题1(by update): NowCoder 295-H
题意,给出n个数( n ≤ 500000 , a i ≤ 500000 n\leq500000,a_i\leq500000 n500000ai500000),要求选出最少的数使异或值为所有数的异或值
解法,用0/1代表x值是否能取到,使用FWT异或卷积,容易发现最多做log次,每一次推复杂度 O ( n ) O(n) O(n),做IDFT时由于只要求一个值,所以复杂度 O ( n ) O(n) O(n),总复杂度 O ( n l o g n ) O(nlogn) O(nlogn)
代码:

#include<cstdio>
#include<cctype>
#include<algorithm>
#define rg register
typedef long long LL;
template <typename T> inline T max(const T a,const T b){return a>b?a:b;}
template <typename T> inline T min(const T a,const T b){return a<b?a:b;}
template <typename T> inline void mind(T&a,const T b){a=a<b?a:b;}
template <typename T> inline void maxd(T&a,const T b){a=a>b?a:b;}
template <typename T> inline T abs(const T a){return a>0?a:-a;}
template <typename T> inline void swap(T&a,T&b){T c=a;a=b;b=c;}
template <typename T> inline T gcd(const T a,const T b){if(!b)return a;return gcd(b,a%b);}
template <typename T> inline T lcm(const T a,const T b){return a/gcd(a,b)*b;}
template <typename T> inline T square(const T x){return x*x;};
template <typename T> inline void read(T&x)
{
    char cu=getchar();x=0;bool fla=0;
    while(!isdigit(cu)){if(cu=='-')fla=1;cu=getchar();}
    while(isdigit(cu))x=x*10+cu-'0',cu=getchar();
    if(fla)x=-x;
}
template <typename T> inline void printe(const T x)
{
    if(x>=10)printe(x/10);
    putchar(x%10+'0');
}
template <typename T> inline void print(const T x)
{
    if(x<0)putchar('-'),printe(-x);
    else printe(x);
}
const int mod=998244353,lenth=524288;
int n,a[lenth],pc[lenth],val,f[lenth];
inline void FWT(int *A)
{
	for(rg int i=1;i<lenth;i<<=1)
		for(rg int j=0;j<lenth;j+=i<<1)
			for(rg int k=0;k<i;k++)
			{
				const int x=A[j+k],y=A[j+k+i];
				A[j+k]=(x+y)%mod;
				A[j+k+i]=(x+mod-y)%mod;
			}
}
int main()
{
	read(n);
	for(rg int i=1,x;i<=n;i++)read(x),a[i]=1,val^=x;
	a[0]=1;
	FWT(a);
	for(rg int i=1;i<lenth;i++)pc[i]=pc[i^(i&-i)]+1;
	for(rg int i=0;i<lenth;i++)f[i]=1;
	for(rg int tim=0;tim<=19;tim++)
	{
		LL calc=0;
		for(rg int i=0;i<lenth;i++)calc+=(pc[i&val]&1)?-f[i]:f[i];
		calc%=mod;
		if(calc)
		{
			print(n-tim);
			return 0;
		}
		for(rg int i=0;i<lenth;i++)f[i]=(LL)f[i]*a[i]%mod;
	}
	return 0;
}

例题2(by update):
我的博客:CF 453 D
撒花结束!
由于写这篇博客写的匆忙,并且很多东西是自己推的,所以如果发现有误请及时提醒我哦

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值