我理解的FWT是在二元运算意义下的卷积

目前比较熟练掌握的集合对称差卷积

对于子集卷积和集合并卷积掌握不是很熟练(挖坑ing)

那么就先来谈一谈集合对称差卷积吧

所谓集合对称差卷积

就是h(i)=sigma(g(j)*f(k))(j^k=i)

首先一个很显然的事情是如下结论:

证明就是如果S是空集,答案为1,否则设存在元素v,则(S交T)和(S交T^v)两两相消配对

答案为0

由于j^k=i,则一定存在j^k^i=0,所以我们可以用上面的式子化简卷积

式子的化简显然是正确的,就是将判断符号带入之后利用乘法分配律分配

这样我们定义:

显然利用反演我们可以得到

至于证明,我们将上面的式子带入下面的式子就可以啦

之后我们再下面的式子代入最上面的式子

可以得到

这也就意味着如果我们可以完成快速沃尔什变换和逆变换

我们就可以在O(n)的时间内把他们乘起来

至于变换的过程,我们利用递推的思想可以得到:

说的简单的一点就是我们FWT的时候利用倍增思想

设左边得到的集合幂级数为tf0

设右边得到的集合幂级数为tf1

合并之后得到的集合幂级数为(tf0+tf1,tf0-tf1)

至于逆变换

得到的集合幂级数为((tf0+tf1)/2,(tf0-tf1)/2)

由于有除法,所以FWT不能对特征为2的集合幂级数使用

以上图片截图自vfk论文

然后一道众人皆知的例题是SRM 518 Nim

在[1,L]中可以重复的选取k个质数,问有多少种方案使得他们异或和为0

我们可以构造集合幂级数,之后我们会发现选取两个我们只需要FWT一次就可以了

选取k个我们就可以使用快速幂了,注意这里并不是对FWT做快速幂

由于集合幂级数并没有次数界的问题,所以可以直接对每个点做快速幂

之后再FWT还原即可

  1. #include<cstdio>
  2. #include<cstring>
  3. #include<iostream>
  4. #include<algorithm>
  5. #include<cstdlib>
  6. using namespace std;
  7.  
  8. typedef long long LL;
  9. const int maxn=200010;
  10. const int mod=1e9+7;
  11. int k,L,m,inv;
  12. bool vis[maxn];
  13. int p[maxn],cnt=0;
  14. int f[maxn];
  15.  
  16. int pow_mod(int v,int p){
  17. int tmp=1;
  18. while(p){
  19. if(p&1)tmp=1LL*tmp*v%mod;
  20. v=1LL*v*v%mod;p>>=1;
  21. }return tmp;
  22. }
  23. void Get_prime(){
  24. for(int i=2;i<=L;++i){
  25. if(!vis[i])p[++cnt]=i;
  26. for(int j=1;j<=cnt;++j){
  27. if(1LL*p[j]*i>L)break;
  28. vis[i*p[j]]=true;
  29. if(i%p[j]==0)break;
  30. }
  31. }return;
  32. }
  33. void FWT(int *A,int n,int flag){
  34. for(int k=1;k<n;k<<=1){
  35. int mk=(k<<1);
  36. for(int j=0;j<n;j+=mk){
  37. for(int i=0;i<k;++i){
  38. int x=A[i+j],y=A[i+j+k];
  39. A[i+j]=1LL*(x+y)*flag%mod;
  40. A[i+j+k]=1LL*(x-y+mod)*flag%mod;
  41. }
  42. }
  43. }return;
  44. }
  45.  
  46. int main(){
  47. freopen("srmnim.in","r",stdin);
  48. freopen("srmnim.out","w",stdout);
  49. scanf("%d%d",&k,&L);
  50. Get_prime();inv=pow_mod(2,mod-2);
  51. for(m=1;m<=L;m<<=1);
  52. for(int i=1;i<=cnt;++i)f[p[i]]=1;
  53. FWT(f,m,1);
  54. for(int i=0;i<m;++i)f[i]=pow_mod(f[i],k);
  55. FWT(f,m,inv);printf("%d\n",f[0]);
  56. return 0;
  57. }

之后是2015年ACM北京赛区的题目

The Celebration of Rabbits

要求你先给每个数赋值在[0,m]范围内,之后再给每个数都加上同一个x,x在[L,R]范围内

求有多少种方案使得最后的数的异或和为0(数的数量为奇数)

首先我们证明一件事情:对于每一种方案中的x都是唯一的

采用反证法,设不是唯一的

设现在序列为a1,a2,a3……,且满足a1^a2^a3……=0

若不是唯一的,则一定存在序列a1-k,a2-k,a3-k……,且他们异或和为0(k!=0)

我们考虑最后一位,由于a1^a2^a3……=0,又因为数的数量为奇数

所以末位为1的有偶数个,末位为0的有奇数个

若k的末位为1,则序列的异或和不为0(因为会转换成末位为1的奇数个,末位为0的有偶数个)

所以k的末位为0,对上一位不会有进位影响,同理我们可以用同样方法考虑上一位

最终得到k=0,与假设不符,证毕

既然x都是唯一的,那么我们可以枚举x,之后问题转换成在[x,x+m]中可以重复的取若干个数使得异或和为0

FWT即可

  1. #include<cstdio>
  2. #include<cstring>
  3. #include<iostream>
  4. #include<cstdlib>
  5. #include<algorithm>
  6. using namespace std;
  7.  
  8. typedef long long LL;
  9. const int mod=1e9+7;
  10. int n,m,L,R;
  11. int ans=0,N,inv;
  12. int f[20010];
  13. int pow_mod(int v,int p){
  14. int tmp=1;
  15. while(p){
  16. if(p&1)tmp=1LL*tmp*v%mod;
  17. v=1LL*v*v%mod;p>>=1;
  18. }return tmp;
  19. }
  20. void FWT(int *A,int n,int flag){
  21. for(int k=1;k<n;k<<=1){
  22. int mk=(k<<1);
  23. for(int j=0;j<n;j+=mk){
  24. for(int i=0;i<k;++i){
  25. int x=A[i+j],y=A[i+j+k];
  26. A[i+j]=1LL*(x+y)*flag%mod;
  27. A[i+j+k]=1LL*(x-y+mod)*flag%mod;
  28. }
  29. }
  30. }return;
  31. }
  32.  
  33. int main(){
  34. inv=pow_mod(2,mod-2);
  35. while(scanf("%d%d%d%d",&n,&m,&L,&R)==4){
  36. ans=0;
  37. for(int i=L;i<=R;++i){
  38. for(N=1;N<=i+m;N<<=1);
  39. for(int j=0;j<N;++j){
  40. if(j>=i&&j<=i+m)f[j]=1;
  41. else f[j]=0;
  42. }
  43. FWT(f,N,1);
  44. for(int j=0;j<N;++j)f[j]=pow_mod(f[j],(n<<1)+1);
  45. FWT(f,N,inv);
  46. ans=ans+f[0];
  47. if(ans>=mod)ans-=mod;
  48. }printf("%d\n",ans);
  49. }return 0;
  50. }

codeforces 259 div1 D

有2^m个点,每个点上有信息,每个时刻每个点会分别向跟他海明码距离为k的点传b(k)次他自己的信息

问t时刻后每个点的信息量

考试题的加强版本,而且数据范围非常全

首先模数的问题可以同考试题的做法解决

首先做法1,我们考虑每个时刻的传输并用集合幂级数表示

设集合幂级数f(i)表示输入数组

设集合幂级数g(j),满足g(j)=b(num(j)) num(j)即j的二进制表示中1的个数

则下一时刻可得h(k)=sigma(f(i)*g(j))(i^j=k)

之后t时刻的话快速幂即可,由于要写快速乘,时间复杂度O(nlog^2n)

用一些奇技淫巧优化快速乘做到O(nlogn)

  1. #include<cstdio>
  2. #include<cstring>
  3. #include<cstdlib>
  4. #include<iostream>
  5. #include<algorithm>
  6. using namespace std;
  7.  
  8. typedef long long LL;
  9. int n,m;
  10. int Num[1050010];
  11. LL t,mod;
  12. LL b[22];
  13. LL f[1050010],g[1050010];
  14.  
  15. inline read(LL &num){
  16. num=0;char ch=getchar();
  17. while(ch<'!')ch=getchar();
  18. while(ch>='0'&&ch<='9')num=num*10+ch-'0',ch=getchar();
  19. }
  20. inline LL mul(LL a,LL b) {
  21. LL tmp=(a*b-(LL)((long double)a/mod*b+1e-8)*mod);
  22. return tmp<0?tmp+mod:tmp;
  23. }
  24. LL pow_mod(LL v,LL p){
  25. LL tmp=1;
  26. while(p){
  27. if(p&1)tmp=mul(tmp,v);
  28. v=mul(v,v);p>>=1;
  29. }return tmp;
  30. }
  31. void FWT(LL *A,int n,int flag){
  32. for(int k=1;k<n;k<<=1){
  33. int mk=(k<<1);
  34. for(int j=0;j<n;j+=mk){
  35. for(int i=0;i<k;++i){
  36. LL x=A[i+j],y=A[i+j+k];
  37. A[i+j]=(x+y)>>flag;
  38. A[i+j+k]=(x-y+mod)>>flag;
  39. if(A[i+j]>=mod)A[i+j]-=mod;
  40. if(A[i+j+k]>=mod)A[i+j+k]-=mod;
  41. }
  42. }
  43. }return;
  44. }
  45.  
  46. int main(){
  47. scanf("%d",&m);read(t);read(mod);
  48. n=(1<<m);mod*=n;
  49. for(int i=0;i<n;++i)read(f[i]),f[i]%=mod;
  50. for(int i=0;i<=m;++i)read(b[i]),b[i]%=mod;
  51. for(int i=0;i<n;++i){
  52. Num[i]=Num[i>>1]+(i&1);
  53. g[i]=b[Num[i]];
  54. }
  55. FWT(f,n,0);FWT(g,n,0);
  56. for(int i=0;i<n;++i){
  57. g[i]=pow_mod(g[i],t);
  58. f[i]=mul(f[i],g[i]);
  59. }
  60. FWT(f,n,1);mod/=n;
  61. for(int i=0;i<n;++i)printf("%I64d\n",f[i]%mod);
  62. return 0;
  63. }

我们还有更优的做法

由于每个点的贡献是可分的,我们不妨考虑每个点t时刻内的贡献系数

同考试题可以证明根据海明码距离我们可以分出m+1个等价类

问题在于我们要搞出单次转移的系数,之后矩阵乘法就可以了

我们考虑海明码距离j对海明码距离i的转移,显然i中对海明码距离贡献为1的点有i个,贡献为0的点有m-i个

设k=i-j,我们选取贡献为1的点x个并取反,选取贡献为0的点y个并取反,一定满足y=x-k

这样操作后两个点的海明码距离为x+y

系数的贡献显然就是b(x+y)*C(i,x)*C(m-i,y)

其中C是组合数,我们枚举x算贡献就可以了

我们矩阵乘法搞出系数来了之后同考试题的做法一样做FWT就可以了

时间复杂度O(m^3logn+nlogn)

  1. #include<cstdio>
  2. #include<cstring>
  3. #include<iostream>
  4. #include<algorithm>
  5. #include<cstdlib>
  6. using namespace std;
  7.  
  8. typedef long long LL;
  9. int m,n;
  10. int Num[1050010];
  11. LL t,mod;
  12. LL C[22][22];
  13. LL f[1050010],g[1050010];
  14. LL b[22];
  15. struct Matrix{
  16. LL a[22][22];
  17. void clear(){memset(a,0,sizeof(a));}
  18. }A,ans;
  19.  
  20. void pre_C(){
  21. C[0][0]=1;
  22. for(int i=1;i<=m;++i){
  23. C[i][0]=C[i][i]=1;
  24. for(int j=1;j<i;++j){
  25. C[i][j]=C[i-1][j-1]+C[i-1][j];
  26. if(C[i][j]>=mod)C[i][j]-=mod;
  27. }
  28. }return;
  29. }
  30. LL mul(LL a,LL b){
  31. LL s=0;
  32. while(b){
  33. if(b&1){
  34. s=s+a;
  35. if(s>=mod)s-=mod;
  36. }
  37. a<<=1;
  38. if(a>=mod)a-=mod;
  39. b>>=1;
  40. }return s;
  41. }
  42. Matrix operator *(const Matrix &A,const Matrix &B){
  43. Matrix C;C.clear();
  44. for(int i=0;i<=m;++i){
  45. for(int j=0;j<=m;++j){
  46. for(int k=0;k<=m;++k){
  47. C.a[i][j]=C.a[i][j]+mul(A.a[i][k],B.a[k][j]);
  48. if(C.a[i][j]>=mod)C.a[i][j]-=mod;
  49. }
  50. }
  51. }return C;
  52. }
  53. Matrix pow_mod(Matrix v,LL p){
  54. Matrix tmp;tmp.clear();
  55. for(int i=0;i<=m;++i)tmp.a[i][i]=1;
  56. while(p){
  57. if(p&1)tmp=tmp*v;
  58. v=v*v;p>>=1;
  59. }return tmp;
  60. }
  61. void FWT(LL *A,int n,int flag){
  62. for(int k=1;k<n;k<<=1){
  63. int mk=(k<<1);
  64. for(int j=0;j<n;j+=mk){
  65. for(int i=0;i<k;++i){
  66. LL x=A[i+j],y=A[i+j+k];
  67. A[i+j]=(x+y)>>flag;
  68. A[i+j+k]=(x-y+mod)>>flag;
  69. if(A[i+j]>=mod)A[i+j]-=mod;
  70. if(A[i+j+k]>=mod)A[i+j+k]-=mod;
  71. }
  72. }
  73. }return;
  74. }
  75.  
  76. int main(){
  77. scanf("%d",&m);n=(1<<m);
  78. scanf("%I64d%I64d",&t,&mod);mod*=n;
  79. pre_C();
  80. for(int i=0;i<n;++i)scanf("%I64d",&f[i]),f[i]%=mod;
  81. for(int i=0;i<=m;++i)scanf("%I64d",&b[i]),b[i]%=mod;
  82. for(int i=0;i<=m;++i){
  83. for(int j=0;j<=m;++j){
  84. int k=i-j;
  85. for(int x=0;x<=i;++x){
  86. int y=x-k;
  87. if(y<0||y>m-i)continue;
  88. int d=x+y;
  89. A.a[j][i]+=mul(b[d],mul(C[i][x],C[m-i][y]));
  90. if(A.a[j][i]>=mod)A.a[j][i]-=mod;
  91. }
  92. }
  93. }
  94. A=pow_mod(A,t);
  95. ans.a[0][0]=1;
  96. ans=ans*A;
  97. for(int i=0;i<n;++i){
  98. Num[i]=Num[i>>1]+(i&1);
  99. g[i]=ans.a[0][Num[i]];
  100. }
  101. FWT(f,n,0);FWT(g,n,0);
  102. for(int i=0;i<n;++i)f[i]=mul(f[i],g[i]);
  103. FWT(f,n,1);mod/=n;
  104. for(int i=0;i<n;++i)printf("%I64d\n",f[i]%mod);
  105. return 0;
  106. }

总结:如何想到FWT?

1、题目中的形式是类似卷积一样的东西,朴素做法O(n^2)算贡献

2、贡献过程是二元运算关系

3、通常情况下算异或和为0神马的QAQ

FWT 学习总结的更多相关文章

  1. 一个数学不好的菜鸡的快速沃尔什变换(FWT)学习笔记

    一个数学不好的菜鸡的快速沃尔什变换(FWT)学习笔记 曾经某个下午我以为我会了FWT,结果现在一丁点也想不起来了--看来"学"完新东西不经常做题不写博客,就白学了 = = 我没啥智 ...

  2. FWT学习笔记

    FWT学习笔记 引入 一般的多项式乘法是这样子的: \(c_i=\sum_{i,j}a_j*b_k*[j+k==i]\) 但是如果我们将这个乘法式子里面的+号变换一下变成其他的运算符号呢? \(c_i ...

  3. FWT 学习笔记

    FWT学习笔记 好久以前写的,先粘上来 定义数组 \(n=2^k\) \(A=[a_0,a_1,a_2,a_3,...,a_{n-1}]\) 令\(A_0=[a_0,a_1,a_2,...,a_{\f ...

  4. FMT/FWT学习笔记

    目录 FMT/FWT学习笔记 FMT 快速莫比乌斯变换 OR卷积 AND卷积 快速沃尔什变换(FWT/XOR卷积) FMT/FWT学习笔记 FMT/FWT是算法竞赛中求or/and/xor卷积的算法, ...

  5. $\text {FWT}$学习笔记

    \(\text {FWT}\) 学习笔记 正常项的\(\text {FWT}\) 在\(\text {OI}\)中,我们经常会碰到这种问题: 给出一个长度为\(n\)的序列\(a_{1,2,...,n ...

  6. 快速沃尔什变换 (FWT)学习笔记

    证明均来自xht37 的洛谷博客 作用 在 \(OI\) 中,\(FWT\) 是用于解决对下标进行位运算卷积问题的方法. \(c_{i}=\sum_{i=j \oplus k} a_{j} b_{k} ...

  7. 快速沃尔什变换 FWT 学习笔记【多项式】

    〇.前言 之前看到异或就担心是 FWT,然后才开始想别的. 这次学了 FWT 以后,以后判断应该就很快了吧? 参考资料 FWT 详解 知识点 by neither_nor 集训队论文 2015 集合幂 ...

  8. 快速沃尔什变换(FWT)学习笔记 + 洛谷P4717 [模板]

    FWT求解的是一类问题:\( a[i] = \sum\limits_{j\bigoplus k=i}^{} b[j]*c[k] \) 其中,\( \bigoplus \) 可以是 or,and,xor ...

  9. 卷积理论 & 高维FWT学习笔记

    之前做了那么多生成函数和多项式卷积的题目,结果今天才理解了优化卷积算法的实质. 首先我们以二进制FWT or作为最简单的例子入手. 我们发现正的FWT or变换就是求$\hat{a}_j=\sum_{ ...

随机推荐

  1. multiple backgrounds 多重背景

    语法缩写如下: background : [background-color] | [background-image] | [background-position][/background-siz ...

  2. LitJSON使用

    地址:http://lbv.github.io/litjson/docs/quickstart.html LitJSON Quickstart Guide Introduction Quick Sta ...

  3. Mysql数据库常用的命令 数据备份 恢复 远程

    远程数据库 格式: mysql -h主机地址 -u用户名 -p用户密码数据库 mysql -h 42.51.150.68 -u yang -p discuz mysql设置密码 mysql>us ...

  4. Linux下vsftpd搭建过程(防火墙版)

    1.确认主机IP [root@www data]# ifconfig  eth0      Link encap:Ethernet  HWaddr 00:0C:29:22:05:B8         ...

  5. 身处IT的你对身边人都有哪些影响

    前不久,跟外甥一起吃饭:他明年就要中考了,我就想,这马上就到人生的关键路口了,看他自己对将来有什么想法没:就问了句:勇勇,你以后想学习哪些方面的东西或者想从事什么工作呢?他简单的说了句:我要跟你一样学 ...

  6. LoadRunner报26612错误的解决方案

    LoadRunner压力测试时,一直会报12261错误,错误内容大概如下: Error -26612: HTTP Status-Code=500 (Internal Server Error) for ...

  7. oracle中的loop与while循环

    Oracle中loop语句会先执行一次循环,然后再判断“exit when”关键字后面的条件表达式的值是true还是false,如果是true,那么将退出循环,否则继续循环. LOOP循环 语法如下l ...

  8. winrar 压缩文件方法

    问题描述: 我要一些大的文件进行压缩,看了网上有很多类拟的写法.但在我这都存在两个问题. 1.就是他们都是通过注册表找到rar的exe目录.我安装好winrar后,虽然注册表有,但那个目录一直报一个错 ...

  9. 二、有限状态机(FSM)

    1.状态机的作用?是什么? 状态机,顾名思义就是用来描述状态的.完善一点就是在同一的时钟下.更准确说是一种verilogHDL编程思想. 例如我们每一个系统都可以分为好几种状态,如:开始,初始化,运行 ...

  10. Careercup - Facebook面试题 - 5729456584916992

    2014-05-02 00:59 题目链接 原题: Given a normal binary tree, write a function to serialize the tree into a ...