【2019牛客多校1 D】二项式展开+FWT原理
参考资料
https://ac.nowcoder.com/discuss/208861?type=101
https://zhuanlan.zhihu.com/p/41867199
http://blog.leanote.com/post/rockdu/TX20
题意
给定n,m,k,再给定n行m列数
求
(修正:这里不是求和是异或和)
分析
以下推导统一用&表示按位与,^表示按位异或
这题考察fwt_xor,但和常规的考察不太一样,它并不是构造式子做卷积,而是关于做FWT后,得到的向量与原向量关系的应用
深入理解FWT_XOR
walsh把信号在不同震荡频率方波下拆解,因此所有的系数都是绝对值大小相同的整数,这使得不需要作浮点数的乘法运算,提高了运算速度。
我信号学得不好,不对上面内容进行理解,以下默认为竞赛应用
首先在这里我们姑且认为,我们需要快速计算
我们设数组A经过快速沃尔什变换之后记作
做变换后满足
考虑到FWT[F] 的每一项是和原来的F线性相关的(因为是只有加减法)
所以可以设
那么我们就是要求
化简可得
这里我们可以看出
设 |x| = (x中1的个数)%2
因为
因为所有系数绝对值大小相同,
所以我们可以确定
也就是
也就是每一个位置对于FWT[F]都有贡献,贡献正负由交集大小的奇偶性确定
题目分析
先考虑
这部分可以写成
即如果连乘式里有一个为偶数,整个式子结果为0
把连乘式展开,得
设S[x]为x个不同元素相加
我们知道
所以上面的x个不同元素相加的每一项可以改写为
所以
这里的f数组即所有相同项的系数和
可以看出这就是沃尔什变换的表达式
所以我们对f数组做沃尔什变换就能够得到count数组
在这里我们需要预处理出来f数组,就枚举计算即可,在这里我用了dfs
代码
#include<bits/stdc .h> using namespace std; const int N = (1<<20) 5; int a[15]; int f[N]; int n,m,k; const int mod = 1e9 7; void dfs(int cs,int now,int base) { if(cs > m) { f[now] =base; return; } dfs(cs 1,now,base); dfs(cs 1,now^a[cs],-base); } void fwt(int a[],int m){ int n = __builtin_ctz(m); for(int i=0;i<n;i ) { for(int j=0;j<m;j ) { if(j&(1<<i)) { int l = a[j^(1<<i)]; int r = a[j]; a[j^(1<<i)] = (l r)%mod; a[j] = (l-r mod)%mod; } } } } int qpow(int a,int b) { int res = 1; while(b) { if(b&1) res = res*1ll*a%mod; a = a*1ll*a%mod; b>>=1; } return res; } int main() { while(scanf("%d%d%d",&n,&m,&k)!=EOF) { int nk = (1<<k); for(int i=0;i<nk;i ) f[i]=0; for(int i=1;i<=n;i ) { for(int j=1;j<=m;j ) { scanf("%d",&a[j]); } dfs(1,0,1); } fwt(f,nk); int base = 1,ans=0; int inv = qpow(1<<m,mod-2); for(int i=0;i<nk;i ) { int now = f[i]*1ll*base%mod*inv %mod; ans ^= now; base = base*3ll%mod; } printf("%d\n",ans); } }