题解 快速莫比乌斯/沃尔什变换 (FMT/FWT)
更新了 FWT-xor 地方关于底数选取的讨论。
更好的阅读体验
当
FMT/FWT 则是处理
快速莫比乌斯变换和莫比乌斯函数/反演并无关系。
FMT 处理
FWT 处理
下面的讲解可能都会带一点集合的味道,但是我不擅长这玩意,有描述不对的地方麻烦指出。
如果我的描述让你感到了疑惑,可以访问 这篇博文 ,也许他的讲解会让你更理解一些。
计算
1.\rm or (并)
考虑对一个序列
如果我们有
我们考虑让
交换枚举的那个意义是先枚举
问题就转化为了如何快速变换了,也就是如何快速求子集和。
那我们考虑一种增量构造的方法。
令
新加入第
如果不选,那么集合是 不选
如果选了,那么集合是 选择
也就是用 选
如图,这个过程可以分成
考虑反过来的过程,我们就是要通过子集和反推原来的数列,那就把加进来的减回去就好了。
void FMT_or(int *f,int n,int op){//op=1 FMT op=-1 IFMT
for(int i=1;i<n;i<<=1)
for(int o=i<<1,j=0;j<n;j+=o)
for(int k=0;k<i;k++)
(f[i+j+k]+=f[j+k]*op+p)%=p;
}
2.\rm{and} (交)
和
最后的改变枚举对象和上面也是类似的,可以不重不漏的枚举到所有的情况,只是换了个形式和 FMT(c) 重合。
也是相似地,我们考虑增量构造的方法求
新加入一个元素
如果 选
如果 不选
由于求和方式的不同,和上面相反,这里需要的是用 不选
反过来的话是类似的,逐步减去即可。
void FMT_and(int *f,int n,int op){//op=1 FMT op=-1 IFMT
for(int i=1;i<n;i<<=1)
for(int o=i<<1,j=0;j<n;j+=o)
for(int k=0;k<i;k++)
(f[j+k]+=f[i+j+k]*op+p)%=p;
}
3.\rm{xor}
这里的
设
然后带入
不难发现,
虽然确实难想到,但是
所以就有
那么就可以
按道理,这里的 -1 是可以换成任意数的,但是稍微把 0,1 这种数带进去就知道有问题。问题出在哪呢?
我们定义 g 的时候就说了,它和 n,i 线性相关,但是当底数是 0,1 的时候,它似乎和 n,i 不太相关。0 只有它的 0 次幂可以定义为 1,但计算起来就会出问题。1 的任何整数次幂都是 1,所以完全和指数没有关系。
那如果放 2 进去当底数呢,它不满足
综合下来,只有 -1 作为底数,便于计算,也便于进行逆运算。
容易证明
依然仿造
新加入一位
选。和选的集合取并,大小不变。和不选的集合取并,大小 -1 。
不选。和选的集合取并,大小不变。和不选的集合取并,大小也不变。
所以
如果 不选,那会累加到原来的选和不选上。
也就是 int x=f[j+k],y=f[i+j+k];f[i+j+k]=x-y,f[j+k]=x+y,就有
逆变换的话是反过来,移一下项,f[i+j+k]=(x-y)/2,f[j+k]=(x+y)/2。
于是就做完了。
void FWT_xor(int *f,int n,int op){//op=1 FWT op=-1 IFWT
for(int i=1;i<n;i<<=1)
for(int o=i<<1,j=0;j<n;j+=o)
for(int k=0;k<i;k++){
int x=f[j+k],y=f[i+j+k];
f[j+k]=(x+y)%p,f[i+j+k]=(x-y+p)%p;
if(op==-1)(f[j+k]*=inv2)%=p,(f[i+j+k]*=inv2)%=p;
}
}
感谢阅读。