一、处理的问题:
给出两个序列\(A,B\),求出序列\(C\),其中\(C_i=\sum\limits_{j\oplus k=i}A_j*B_k\),其中\(\oplus\)是一种运算。
二、举例
我们运用\(FFT\)的思想,考虑先对\(A,B\)构造一个序列\(FWT(A)\)和\(FWT(B)\),满足\(FWT(C)_i=FWT(A)_i*FWT(B)_i\),之后再从\(FWT(C)\)转为\(C\)。
拿或运算举例:
如果有\(k=i\mid j\),那么\(i,j\)必定是\(k\)的子集。
我们首先构造\(FWT(A)_i=A'_i=\sum\limits_{i=i|j}A_j\),表示\(j\)是\(i\)的子集。
那么就会有:
\(C'_i=\sum\limits_{i=i|(j|k)}C_{j|k}=\sum\limits_{i=i|j}A_j*\sum\limits_{i=i|k}B_k=A'_i*B'_i\)
即:
\(FWT(C)_i=FWT(A)_i*FWT(B)_i\)
现在考虑怎么求\(FWT(A)\):
暴力显然是\(O(n^2)\)的(对每个\(i\)枚举\(j\)),我们考虑分治。
这时我们将\(A\)一分为二,我们令\(A_1\)表示序列\(A\)的前半段,\(A_2\)表示\(A\)的后半段。
这时我们发现:
\(FWT(A)=merge(FWT(A_1),FWT(A_1)+FWT(A_2))\)
其中\(merge\)表示将两个序列前后拼接在一起,\(+\)表示每一位相加。
因为对于\(FWT(A)\)的前半段,因为当前二进制最高位为\(0\),所以它就等于\(FWT(A_1)\)。
而对于\(FWT(A)\)的后半段,首先它肯定有\(FWT(A_2)\),我们考虑\(A_1\)中的元素对\(FWT(A)\)后半段的贡献。
我们设\(len(A_1)=len(A_2)=l\)。
那么对于\(i
于是我们可以递归分治\(O(nlogn)\)来进行\(FWT\)。
我们求出\(FWT(C)\)之后还要进行逆变换,于是我们考虑\(IFWT\)怎么做。
其实\(IFWT\)很简单,我们只要按照刚才\(FWT\)时的过程逆着来一遍即可。
\(IFWT(C)=merge(IFWT(C_1),IFWT(C_2)-IFWT(C_1))\)
三、推广
更广泛地说,我们定义\(\oplus\)的\(FWT\)为:
\(A'=\{\sum\limits_{i\oplus j=0}A_i*B_j,\sum\limits_{i\oplus j=1}A_i*B_j,...,\sum\limits_{i\oplus j=n-1}A_i*B_j\}\)
现在给出其它运算的\(FWT\)求法:
与或运算相同,我们可以推出与运算的求法:
\(FWT(A)=merge(FWT(A_1)+FWT(A_2),FWT(A_2))\)
\(IFWT(A)=merge(IFWT(A_1)-IFWT(A_2),IFWT(A_2))\)
异或运算:
直接抄结论(不会证):
\(FWT(A)=(FWT(A_1)+FWT(A_2),FWT(A_1)-FWT(A_2))\)
\(IFWT(A)=(\frac{FWT(A_1)+FWT(A_2)}{2},\frac{FWT(A_1)-FWT(A_2)}{2})\)
同或(我今天才知道有这么个东西):
\(FWT(A)=(FWT(A_2)-FWT(A_1),FWT(A_2)+FWT(A_1))\)
\(IFWT(A)=(\frac{FWT(A_2)-FWT(A_1)}{2},\frac{FWT(A_2)+FWT(A_1)}{2})\)
模板题
code:
#include
using namespace std;
typedef long long ll;
const int maxn=(1<<17)+10;
const ll mod=998244353;
const ll inv2=mod+1>>1;
int n,m;
ll A[maxn],B[maxn],a[maxn],b[maxn],c[maxn];
inline ll read()
{
char c=getchar();ll res=0,f=1;
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9')res=res*10+c-'0',c=getchar();
return res*f;
}
void fwt_or(ll* a,int op)
{
for(int mid=1;mid