FWT
熟知两个序列 \(A=\{a_i\}_{i=0}^n\),\(B=\{b_i\}_{i=0}^n\) 的加法卷积为:
\[c_i=\sum\limits_{j+k=i}^na_jb_k \]计算方式是:
\[A^\prime=FFT(A),B^\prime=FFT(B) \\ C=A^\prime\times B^\prime,IFFT(C) \]那如果我们要计算or卷积,and卷积或是xor卷积呢?
也就是说我们要计算:
\[c_i=\sum\limits_{j\oplus k=i}^na_jb_k \]其中,\(\oplus\) 表示一种位运算,可以解决吗?
可以,计算方式是:
\[A^\prime=FWT(A),B^\prime=FWT(B) \\ C=A^\prime\times B^\prime,IFWT(C) \]or卷积
我们将一个多项式 \(A=a_0+a_1x+\cdots+a_nx^n\) 分成两部分:\(A_0,A_1\)
其中 \(A_0\) 就是多项式前面 \(2^{n-1}\) 项,也就是高位为 \(0\) 的部分(所以下标为 \(0\) 啊)
类似的,\(A_1\) 就是后 \(2^{n-1}\) 项
显然对于 \(A_0\) 与 \(A_1\),它们都是一个 \(2^{n-1}\) 维向量,于是我们定义 \((A,B)\) 为它们的合成,即一个 \(2^n\) 维向量:
\[(A,B)=(a_0,a_1,\cdots,a_{n},b_1,b_2,\cdots,b_n) \]于是 \(A=(A_0,A_1)\)
类似FFT分治思想,我们先考虑最高位
对于一个最高位为 \(0\) 的指标 \(i\),显然它与最高位为 \(1\) 的指标or的时候,最高位一定为 \(1\)
对于一个最高位为 \(1\) 的指标 \(i\),它怎么or最高位都是 \(1\)
于是对于一个长度为 \(2^n\) 的 \(A\):
\[FWT(A)= \begin{cases} (FWT(A_0),FWT(A_0+A_1))&n\ge0 \\ A&n=0 \end{cases} \]我们不妨把这个叫做分解式,下同
其中的 \(+\) 就是普通意义下的多项式加法
对于 \(FWT(A+B)\),我们有:
\[\begin{align*} FWT(A+B)&=\sum\limits_{j|k=i}^n(a_j+b_j) \\ &=\sum\limits_{j|k=i}^na_j+\sum\limits_{j|k=i}^nb_j \\ &=FWT(A)+FWT(B) \end{align*} \]对于 \(FWT(A,B)\),依照上文定义显然有:
\[FWT(A)=(FWT(A_0),FWT(A_0+A_1))\Rightarrow FWT(A,B)=(FWT(A),FWT(A+B)) \]现在我们要证明的就是 \(FWT(A|B)=FWT(A)\times FWT(B)\)
根据分治的特点,这里显然选用数学归纳法更加简便一些
显然 \(n=0\) 时成立,于是:
\[\begin{align*} &FWT(A|B)\\&=FWT((A|B)_0,(A|B)_1) \\ &=FWT(A_0|B_0,A_0|B_1+A_1|B_0+A_1|B_1) \\ &=(FWT(A_0|B_0),FWT(A_0|B_0+A_0|B_1+A_1|B_0+A_1|B_1)) \\ &=(FWT(A_0)\times FWT(B_0),\sum\limits_{i=0}^1\sum\limits_{j=0}^1(FWT(A_i)\times FWT(B_j))) \\ &=(FWT(A_0)\times FWT(B_0),(FWT(A_0)+FWT(A_1))\times(FWT(B_0)+FWT(B_1)) \\ &=(FWT(A_0)\times FWT(B_0),(FWT(A_0+A_1))\times(FWT(B_0+B_1)) \\ &=(FWT(A_0),FWT(A_0+A_1))\times(FWT(B_0),FWT(B_0+B_1))\tag{1} \\ &=FWT(A)\times FWT(B) \end{align*} \]这里的 \(\times\) 似乎是对应位置相乘,因为如果做加法卷积 \((1)\) 处是不成立的
然后根据分解式,我们有:
\[IFWT(FWT(A))=A\\\Rightarrow IFWT(FWT(A_0),FWT(A_0+A_1))=A \\\Rightarrow IFWT(A)=(IFWT(A_0),IFWT(A_1-A_0)) \]and卷积
依然沿用上文的思想与记号
如果一个指标的最高位为 \(0\),那么它怎么and都是 \(0\)
如果一个指标的最高位为 \(1\),那么它与最高位为 \(0\) 的and的时候,最高位为 \(0\),与最高位为 \(1\) 的and的时候,最高位为 \(1\)
于是:
\[FWT(A)= \begin{cases} (FWT(A_0+A_1),FWT(A_1))&n\ge0 \\ A&n=0 \end{cases} \]类似的,对于 \(FWT(A,B)\):
\[FWT(A,B)=(FWT(A+B),FWT(B)) \] \[\begin{align*} &FWT(A\&B)\\&=FWT((A\&B)_0,(A\&B_1)) \\ &=FWT(A_0\&B_0+A_0\&B_1+A_1\&B_0,A_1\&B_1) \\ &=(FWT(A_0\&B_0+A_0\&B_1+A_1\&B_0+A_1\&B_1),FWT(A_1\&B_1)) \\ &=(FWT(A_0+A_1),FWT(A_1))\times (FWT(B_0+B_1),FWT(B_1)) \\ &=FWT(A)\times FWT(B) \end{align*} \]仿照or,我们有:
\[IFWT(A)=(IFWT(A_0-A_1),IFWT(A_1)) \]xor卷积
最高位相同的xor结果为 \(0\),最高位不同的xor结果为 \(1\)
首先我们定义:
\[FWT(A)_i=\sum\limits_{\text{popcount}(i\&j)\equiv0\bmod 2}a_j-\sum\limits_{\text{popcount}(i\&j)\equiv1\bmod 2}a_j \]其中,\(FWT(A)_i\) 表示 \(FWT(A)\) 的第 \(i\) 项系数
于是
\[FWT(A)= \begin{cases} (FWT(A_0+A_1),FWT(A_0-A_1))&n\ge0 \\ A&n=0 \end{cases} \]于是有:
\[\begin{aligned} &FWT(A\oplus B)\\&=(FWT(A\oplus B)_0+FWT(A\oplus B)_1,FWT(A\oplus B)_0-FWT(A\oplus B)_1) \\ &=(FWT(A_0\oplus B_0+A_1\oplus B_0+A_0\oplus B_1+A_1\oplus B_1),FWT(A_0\oplus B_0+A_1\oplus B_1-A_1\oplus B_0-A_0\oplus B_1)) \\ &=((FWT(A_0)+FWT(A_1))\times(FWT(B_0)+FWT(B_1)),(FWT(A_0)-FWT(A_1))\times(FWT(B_0)-FWT(B_1)) \\ &=(FWT(A_0+A_1),FWT(A_0-A_1))\times(FWT(B_0+B_1),FWT(B_0-B_1)) \\ &=FWT(A)\times FWT(B) \end{aligned} \]依然是根据分解式,我们有:
\[IFWT(FWT(A))=A\\\Rightarrow IFWT(FWT(A_0+A_1),FWT(A_0-A_1))=A \\\Rightarrow IFWT(A)=(\frac{IFWT(A_0+A_1)}{2},\frac{IFWT(A_0-A_1)}{2}) \]代码实现
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#define N 1000001
#define M 5001
#define INF 1100000000
#define Kafuu return
#define Chino 0
#define R register
#define C const
#define U unsigned
#define fx(l,n) inline l n
#define set(l,n,ty,len) memset(l,n,sizeof(ty)*len)
#define cpy(f,t,ty,len) memcpy(t,f,sizeof(ty)*len)
#define int long long
using namespace std;
C int mod=998244353;
fx(int,gi)(){
R char c=getchar();R int s=0,f=1;
while(c>'9'||c<'0'){
if(c=='-') f=-f;
c=getchar();
}
while(c<='9'&&c>='0') s=(s<<3)+(s<<1)+(c-'0'),c=getchar();
return s*f;
}
int n,F[N],G[N],f[N],g[N],wn;
fx(void,FWTor)(int *f,short op,int x){
R int len,hl,s,i;
for(len=2,hl=1;len<=x;hl=len,len<<=1){
for(s=0;s<x;s+=len){
for(i=0;i<hl;i++){
(f[s|i|hl]+=f[s|i]*op+mod)%=mod;
}
}
}
}
fx(void,FWTand)(int *f,short op,int x){
R int len,hl,s,i;
for(len=2,hl=1;len<=x;hl=len,len<<=1){
for(s=0;s<x;s+=len){
for(i=0;i<hl;i++){
(f[s|i]+=f[s|i|hl]*op+mod)%=mod;
}
}
}
}
fx(void,FWTxor)(int *f,int op,int x){
R int len,hl,s,i,uni;
for(len=2,hl=1;len<=x;hl=len,len<<=1){
for(s=0;s<x;s+=len){
for(i=0;i<hl;i++){
uni=f[s|i]-f[s|i|hl];
f[s|i]=(f[s|i]+f[s|i|hl])*op%mod;
f[s|i|hl]=(uni+mod)*op%mod;
}
}
}
}
signed main(){
n=gi();wn=1<<n;
for(R int i=0;i<(1<<n);i++) F[i]=gi();
for(R int i=0;i<(1<<n);i++) G[i]=gi();
cpy(F,f,int,wn);cpy(G,g,int,wn);
FWTor(f,1,wn);FWTor(g,1,wn);
for(R int i=0;i<wn;i++) (f[i]*=g[i])%=mod;
FWTor(f,-1,wn);
for(R int i=0;i<wn;i++) printf("%lld ",f[i]);
printf("\n");
cpy(F,f,int,wn);cpy(G,g,int,wn);
FWTand(f,1,wn);FWTand(g,1,wn);
for(R int i=0;i<wn;i++) (f[i]*=g[i])%=mod;
FWTand(f,-1,wn);
for(R int i=0;i<wn;i++) printf("%lld ",f[i]);
printf("\n");
cpy(F,f,int,wn);cpy(G,g,int,wn);
FWTxor(f,1,wn);FWTxor(g,1,wn);
for(R int i=0;i<wn;i++) (f[i]*=g[i])%=mod;
FWTxor(f,(mod+1)>>1,wn);
for(R int i=0;i<wn;i++) printf("%lld ",f[i]);
printf("\n");
}
CF449D Jzzhu and Numbers
题目分析
说实话,and背包能想出来,\(n\) 次FWT也能想出来,但是接下来怎么做确实不会,应该还是理解不透彻&不熟练...
首先这显然是个背包模型:
\[f_{i,j}=\sum\limits_{a_i\&k=j}f_{i-1,k}+f_{i-1,j} \]观察式子,我们发现左边跟FWT正变换有亿点点相似,暴力做 \(n\) 次FWT即可
众所周知,对于and卷积,其实就是求一个指标 \(i\) 的指标超集对应的数的和
于是我们循环对每一项置 \(1\) 后FWT的结果数列中肯定没有大于二的数
我们可以根据这个性质来优化此题:
- 如果这一项是 \(0\),也就是说不做贡献
- 如果这一项是 \(1\),也就是说这一项翻倍
不断翻倍,也就是说贡献为 \(2\) 的幂次方倍,我们只要知道翻倍翻了几次
这简单,找超集个数即整体FWT
观察式子:
\[FWT(A)_i=\sum\limits_{j\&k=i}a_j \]于是我们在每一位上累计 \(2\) 的幂次方倍,然后IFWT即可
//ysh SB!!!
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#define N 3000001
#define M 5001
#define INF 1100000000
#define Kafuu return
#define Chino 0
#define R register
#define C const
#define U unsigned
#define fx(l,n) inline l n
#define set(l,n,ty,len) memset(l,n,sizeof(ty)*len)
#define cpy(f,t,ty,len) memcpy(t,f,sizeof(ty)*len)
#define int long long
using namespace std;
C int mod=1e9+7;
fx(int,gi)(){
R char c=getchar();R int s=0,f=1;
while(c>'9'||c<'0'){
if(c=='-') f=-f;
c=getchar();
}
while(c<='9'&&c>='0') s=(s<<3)+(s<<1)+(c-'0'),c=getchar();
return s*f;
}
int n,a[N],t[N],sum[N],x=1,maxn;
bool b;
fx(int,fpow)(int a,int b){
int sum=1;
while(b){
if(b&1) (sum*=a)%=mod;
(a*=a)%=mod;
b>>=1;
}
return sum;
}
fx(void,FWTand)(int *f,short op,int x){
R int hl,len,i,s;
for(len=2,hl=1;len<=x;hl=len,len<<=1){
for(s=0;s<x;s+=len){
for(i=0;i<hl;i++){
(f[s|i]+=f[s|i|hl]*op+mod)%=mod;
}
}
}
}
signed main(){
n=gi();
for(R int i=1;i<=n;i++) a[i]=gi(),t[a[i]]+=1,b|=a[i],maxn=max(maxn,a[i]);
while(x<=maxn) x<<=1;
FWTand(t,1,x);
for(R int i=0;i<x;i++) t[i]=fpow(2,t[i])%mod;
FWTand(t,-1,x);
if(b) printf("%lld",t[0]);
else printf("%lld",t[0]-1);
}