大家好,欢迎来到IT知识分享网。
用途
F W T FWT FWT 用来在 O ( n log 2 n ) O(n\log_2 n) O(nlog2n) 的时间内解决形如 C k = ∑ i ⊕ j = k A i B j C_k=\sum_{i\oplus j=k}A_iB_j Ck=∑i⊕j=kAiBj 的位运算卷积问题。
其中 ⊕ \oplus ⊕ 表示 and , or , xor , nxor \operatorname{and},\operatorname{or},\operatorname{xor},\operatorname{nxor} and,or,xor,nxor 等位运算中的一种。
思路和 F F T FFT FFT 类似,都是先把多项式 A , B A,B A,B 写成类似 点值表示法 的形式,分别记为 F W T ( A ) FWT(A) FWT(A) 和 F W T ( B ) FWT(B) FWT(B) ,使得 F W T ( C ) i = F W T ( A ) i ⋅ F W T ( B ) i FWT(C)_i=FWT(A)_i\cdot FWT(B)_i FWT(C)i=FWT(A)i⋅FWT(B)i 。
现在按位运算进行分类讨论。
一些符号
这里作一些规定。
- 每一个序列的长度都是二的整数幂的;
- 下文中默认位运算的优先级在 + + + 之前,即会先计算位运算,再计算加法;
- ( A , B ) (A,B) (A,B) 表示把 A , B A,B A,B 按 A A A 在前, B B B 在后的顺序拼接起来组成一个长度为 ∣ A ∣ + ∣ B ∣ |A|+|B| ∣A∣+∣B∣ 的序列;
- A + B A+B A+B 表示把两个长度相等的序列 A , B A,B A,B 的每一个对应下标上的数相加,即 ( A + B ) i = A i + B i (A+B)_i=A_i +B_i (A+B)i=Ai+Bi ,形成的序列长度为 ∣ A ∣ |A| ∣A∣ ;
- A − B A-B A−B 表示把两个长度相等的序列 A , B A,B A,B 的每一个对应下标上的数相减,即 ( A − B ) i = A i − B i (A-B)_i=A_i -B_i (A−B)i=Ai−Bi ,形成的序列长度为 ∣ A ∣ |A| ∣A∣ 。
不同位运算下的形式
与运算
先给出一个结论: F W T ( A ) i = ∑ i and j = i A j FWT(A)_i=\sum_{i\operatorname{and} j=i} A_j FWT(A)i=iandj=i∑Aj
证明:
∀ i ∈ [ 0 , ∣ A ∣ ) ∪ Z , 都有 F W T ( A ) i × F W T ( B ) i = F W T ( C ) i ⟸ ( ∑ j and i = i A j ) ( ∑ k and i = i B k ) = ∑ j and k and i = i A j B k ⟸ { j and i = i , k and i = i 等价于 j and k and i = i \begin{aligned} &\forall i\in[0,|A|)\cup Z,\text{都有}\\ &\uad\uad FWT(A)_i\times FWT(B)_i=FWT(C)_i\\ &\impliedby\left(\sum_{j\operatorname{and} i=i}A_j\right)\left(\sum_{k\operatorname{and} i=i}B_k \right) =\sum_{j\operatorname{and} k\operatorname{and} i=i}A_jB_k\\ &\impliedby\begin{cases} j\operatorname{and} i&=i,\\ k\operatorname{and} i&=i \end{cases} \quad\text{等价于}\quad j\operatorname{and} k\operatorname{and} i=i \end{aligned} ∀i∈[0,∣A∣)∪Z,都有FWT(A)i×FWT(B)i=FWT(C)i⟸
jandi=i∑Aj
(kandi=i∑Bk)=jandkandi=i∑AjBk⟸{
jandikandi=i,=i等价于jandkandi=i得证。
根据这个结论直接做是 O ( n 2 ) O\left(n^2\right) O(n2) 的。
考虑像 F F T FFT FFT 那样,把序列 A A A 二分为序列 A 0 , A 1 A_0,A_1 A0,A1 ,再根据 F W T ( A 0 ) , F W T ( A 1 ) FWT(A_0),FWT(A_1) FWT(A0),FWT(A1) 算出 F W T ( A ) FWT(A) FWT(A) ,这样就可以做到 O ( n log 2 n ) O(n\log_2 n) O(nlog2n) 了。
发现如果把序列 A A A 的下标用 二进制 表示, A 0 A_0 A0 是由下标中第 1 + log 2 ∣ A ∣ 1+\log_2 |A| 1+log2∣A∣ 位(也就是当前的最高位)为 0 0 0 的元素组成的, A 1 A_1 A1 中的元素的最高位则为 1 1 1 。
因为 and \operatorname{and} and 运算有这样的性质:除了 1 and 1 = 1 1\operatorname{and} 1=1 1and1=1 ,其它 0 and 1 , 0 and 0 , 1 and 0 0\operatorname{and} 1,0\operatorname{and} 0,1\operatorname{and} 0 0and1,0and0,1and0 的值都为 0 0 0 。即 A 1 A_1 A1 会贡献到 A 0 A_0 A0 ,因此可以得到:
F W T ( A ) = ( F W T ( A 0 ) + F W T ( A 1 ) , F W T ( A 1 ) ) FWT(A)=(FWT(A_0)+FWT(A_1),FWT(A_1)) FWT(A)=(FWT(A0)+FWT(A1),FWT(A1))
那反演就容易了:
U F W T ( A ) = ( U F W T ( A 0 ) − U F W T ( A 1 ) , U F W T ( A 1 ) ) UFWT(A)=(UFWT(A_0)-UFWT(A_1),UFWT(A_1)) UFWT(A)=(UFWT(A0)−UFWT(A1),UFWT(A1))
代码
inline void fwt_and(int f[],int opt) {
for(int i=2,j=1;i<=m;j=i,i<<=1) for(int t=0;t<m;t+=i) for(int k=t;k<j+t;++k) f[k]+=f[k+j]*opt; }
或运算
同样的,先给出结论: F W T ( A ) i = ∑ i or j = i A j FWT(A)_i=\sum_{i\operatorname{or} j=i} A_j FWT(A)i=iorj=i∑Aj
证明是类似的,就不写了。结论也是类似的:
F W T ( A ) = ( F W T ( A 0 ) , F W T ( A 0 ) + F W T ( A 1 ) ) U F W T ( A ) = ( U F W T ( A 0 ) , U F W T ( A 1 ) − U F W T ( A 0 ) ) \begin{aligned} FWT(A)&=(FWT(A_0),FWT(A_0)+FWT(A_1))\\ UFWT(A)&=(UFWT(A_0),UFWT(A_1)-UFWT(A_0)) \end{aligned} FWT(A)UFWT(A)=(FWT(A0),FWT(A0)+FWT(A1))=(UFWT(A0),UFWT(A1)−UFWT(A0))
代码
inline void fwt_or(int f[],int opt) {
for(int i=2,j=1;i<=m;j=i,i<<=1) for(int t=0;t<m;t+=i) for(int k=t;k<j+t;++k) f[k+j]+=f[k]*opt; }
异或运算
注意这个结论长得不一样( popcount ( x ) \operatorname{popcount}(x) popcount(x) 表示把 x x x 写成二进制形式有多少位上的数为 1 1 1 ):
F W T ( A ) i = ∑ 2 ∣ popcount ( i and j ) A j − ∑ 2 ∤ popcount ( i and j ) A j FWT(A)_i=\sum_{2\mid\operatorname{popcount}(i\operatorname{and} j)} A_j -\sum_{2\nmid\operatorname{popcount}(i\operatorname{and} j)} A_j FWT(A)i=2∣popcount(iandj)∑Aj−2∤popcount(iandj)∑Aj
注意不是直接仿照 与运算 、 或运算 的形式写成
F W T ( A ) i = ∑ i xor j = i A j FWT(A)_i=\sum_{i\operatorname{xor} j=i} A_j FWT(A)i=ixorj=i∑Aj
为了方便起见,定义运算符 ⊗ \otimes ⊗ : x ⊗ y = popcount ( x and y ) m o d 2 x\otimes y=\operatorname{popcount}(x\operatorname{and} y)\mod{2} x⊗y=popcount(xandy)mod2 。
那么这个运算符是满足以下性质的:
( x ⊗ y ) xor ( x ⊗ z ) = x ⊗ ( y xor z ) (x\otimes y)\operatorname{xor}(x\otimes z)=x\otimes (y\operatorname{xor} z) (x⊗y)xor(x⊗z)=x⊗(yxorz)
证明:
令 a = popcount ( x and y and z ) , b = popcount ( x xor y ) − a , c = popcount ( y xor z ) − a a=\operatorname{popcount}(x\operatorname{and}y\operatorname{and}z),b=\operatorname{popcount}(x\operatorname{xor}y)-a,c=\operatorname{popcount}(y\operatorname{xor}z)-a a=popcount(xandyandz),b=popcount(xxory)−a,c=popcount(yxorz)−a 。
那么
popcount ( x and ( y xor z ) ) = b + c = popcount ( x xor y ) + popcount ( x xor z ) − 2 a ∴ x ⊗ ( y xor z ) = ( x ⊗ y + x ⊗ z ) m o d 2 = ( x ⊗ y ) xor ( x ⊗ z ) \begin{aligned} &\quad\;\operatorname{popcount}(x\operatorname{and}(y\operatorname{xor}z))\\&=b+c\\ &=\operatorname{popcount}(x\operatorname{xor}y)+\operatorname{popcount}(x\operatorname{xor}z)-2a\\ &\therefore x\otimes(y\operatorname{xor}z)\\ &=(x\otimes y+x\otimes z)\mod{2}\\ &=(x\otimes y)\operatorname{xor}(x\otimes z) \end{aligned} popcount(xand(yxorz))=b+c=popcount(xxory)+popcount(xxorz)−2a∴x⊗(yxorz)=(x⊗y+x⊗z)mod2=(x⊗y)xor(x⊗z)
得证。
然后现在来证明结论:
证明:
F W T ( A ) i ⋅ F W T ( B ) i = ( ∑ j ⊗ i = 0 A j − ∑ j ⊗ i = 1 A j ) ( ∑ k ⊗ i = 0 B k − ∑ k ⊗ i = 1 B k ) = ( ∑ j ⊗ i = 0 A j ) ( ∑ k ⊗ i = 0 B k ) − ( ∑ j ⊗ i = 0 A j ) ( ∑ k ⊗ i = 1 B k ) − ( ∑ j ⊗ i = 1 A j ) ( ∑ k ⊗ i = 0 B k ) + ( ∑ j ⊗ i = 1 A j ) ( ∑ k ⊗ i = 1 B k ) = ∑ i ⊗ ( j xor k ) = 0 A j B k − ∑ i ⊗ ( j xor k ) = 1 A j B k = F W T ( C ) i \begin{aligned} &FWT(A)_i\cdot FWT(B)_i\\ =&\left(\sum_{j\otimes i=0}A_j-\sum_{j\otimes i=1}A_j \right)\left(\sum_{k\otimes i=0}B_k-\sum_{k\otimes i=1}B_k \right)\\ =&\left(\sum_{j\otimes i=0}A_j\right)\left(\sum_{k\otimes i=0}B_k\right)-\left(\sum_{j\otimes i=0}A_j\right)\left(\sum_{k\otimes i=1}B_k\right)-\\ &\left(\sum_{j\otimes i=1}A_j\right)\left(\sum_{k\otimes i=0}B_k\right)+\left(\sum_{j\otimes i=1}A_j\right)\left(\sum_{k\otimes i=1}B_k\right)\\ =&\sum_{i\otimes(j\operatorname{xor}k)=0}A_jB_k-\sum_{i\otimes(j\operatorname{xor}k)=1}A_jB_k\\ =&FWT(C)_i \end{aligned} ====FWT(A)i⋅FWT(B)i(j⊗i=0∑Aj−j⊗i=1∑Aj)(k⊗i=0∑Bk−k⊗i=1∑Bk)(j⊗i=0∑Aj)(k⊗i=0∑Bk)−(j⊗i=0∑Aj)(k⊗i=1∑Bk)−(j⊗i=1∑Aj)(k⊗i=0∑Bk)+(j⊗i=1∑Aj)(k⊗i=1∑Bk)i⊗(jxork)=0∑AjBk−i⊗(jxork)=1∑AjBkFWT(C)i
得证。
因此可以得出:
F W T ( A ) = ( F W T ( A 0 ) + F W T ( A 1 ) , F W T ( A 0 ) − F W T ( A 1 ) ) U F W T ( A ) = ( U F W T ( A 1 ) + U F W T ( A 0 ) 2 , U F W T ( A 1 ) − U F W T ( A 0 ) 2 ) \begin{aligned} FWT(A)&=(FWT(A_0)+FWT(A_1),FWT(A_0)-FWT(A_1))\\ UFWT(A)&=\left(\cfrac{UFWT(A_1)+UFWT(A_0)}{2},\cfrac{UFWT(A_1)-UFWT(A_0)}{2}\right) \end{aligned} FWT(A)UFWT(A)=(FWT(A0)+FWT(A1),FWT(A0)−FWT(A1))=(2UFWT(A1)+UFWT(A0),2UFWT(A1)−UFWT(A0))
代码
这份代码里面的 F W T FWT FWT 是在取模意义下的,不用取模的话修改一下就好了。
inline int plus(int x,int y) {
x+=y; if(x>=P) x-=P; else if(x<0) x+=P; return x; } inline void fwt_xor(int f[],int opt) {
for(int i=2,j=1,x,y;i<=m;j=i,i<<=1) for(int t=0;t<m;t+=i) for(int k=t;k<t+j;++k) {
x=plus(f[k],f[k+j]),y=plus(f[k],P-f[k+j]); opt>0? f[k]=x,f[k+j]=y : (f[k]=inv2*x%P,f[k+j]=inv2*y%P) ; } }
同或运算
同或运算下的 F W T FWT FWT 与异或的类似。
先给出结论: F W T ( A ) i = ∑ 2 ∣ popcount ( i or j ) A j − ∑ 2 ∤ popcount ( i or j ) A j FWT(A)_i=\sum_{2\mid\operatorname{popcount}(i\operatorname{or} j)} A_j -\sum_{2\nmid\operatorname{popcount}(i\operatorname{or} j)} A_j FWT(A)i=2∣popcount(iorj)∑Aj−2∤popcount(iorj)∑Aj
可以得到
F W T ( A ) = ( F W T ( A 0 ) − F W T ( A 1 ) , F W T ( A 0 ) + F W T ( A 1 ) ) U F W T ( A ) = ( U F W T ( A 1 ) − U F W T ( A 0 ) 2 , U F W T ( A 1 ) + U F W T ( A 0 ) 2 ) \begin{aligned} FWT(A)&=(FWT(A_0)-FWT(A_1),FWT(A_0)+FWT(A_1))\\ UFWT(A)&=\left(\cfrac{UFWT(A_1)-UFWT(A_0)}{2},\cfrac{UFWT(A_1)+UFWT(A_0)}{2}\right) \end{aligned} FWT(A)UFWT(A)=(FWT(A0)−FWT(A1),FWT(A0)+FWT(A1))=(2UFWT(A1)−UFWT(A0),2UFWT(A1)+UFWT(A0))
在异或运算的代码上改一下可以了。
例题
P4717
P4717 【模板】快速莫比乌斯/沃尔什变换 (FMT/FWT)
#include<cstdio> #include<cstring> using namespace std; #define fo(i,l,r) for(i=l;i<r;++i) #define N const int P=; const long long inv2=; int m,a[N],b[N],c[N],A[N],B[N]; inline void add(int &x,int y) {
x+=y; if(x>=P) x-=P; else if(x<0) x+=P; } inline int plus(int x,int y) {
x+=y; if(x>=P) x-=P; else if(x<0) x+=P; return x; } inline void fwt_or(int f[],int opt) {
for(int i=2,j=1;i<=m;j=i,i<<=1) for(int t=0;t<m;t+=i) for(int k=t;k<j+t;++k) add(f[k+j],f[k]*opt); } inline void fwt_and(int f[],int opt) {
for(int i=2,j=1;i<=m;j=i,i<<=1) for(int t=0;t<m;t+=i) for(int k=t;k<j+t;++k) add(f[k],f[k+j]*opt); } inline void fwt_xor(int f[],int opt) {
for(int i=2,j=1,x,y;i<=m;j=i,i<<=1) for(int t=0;t<m;t+=i) for(int k=t;k<t+j;++k) {
x=plus(f[k],f[k+j]),y=plus(f[k],P-f[k+j]); opt>0? f[k]=x,f[k+j]=y : (f[k]=inv2*x%P,f[k+j]=inv2*y%P) ; } } int main() {
freopen("fwt.in","r",stdin); freopen("fwt.out","w",stdout); int n,i; scanf("%d",&n),m=1<<n; fo(i,0,m) scanf("%d",a+i); fo(i,0,m) scanf("%d",b+i); fo(i,0,m) A[i]=a[i],B[i]=b[i]; fwt_or(a,1),fwt_or(b,1); fo(i,0,m) c[i]=1LL*a[i]*b[i]%P; fwt_or(c,-1); fo(i,0,m) printf("%d ",c[i]);puts(""); memcpy(a,A,sizeof a),memcpy(b,B,sizeof b); fwt_and(a,1),fwt_and(b,1); fo(i,0,m) c[i]=1LL*a[i]*b[i]%P; fwt_and(c,-1); fo(i,0,m) printf("%d ",c[i]);puts(""); memcpy(a,A,sizeof a),memcpy(b,B,sizeof b); fwt_xor(a,1),fwt_xor(b,1); fo(i,0,m) c[i]=1LL*a[i]*b[i]%P; fwt_xor(c,-1); fo(i,0,m) printf("%d ",c[i]);puts(""); return 0; }
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/128345.html