【笔记】FWT 快速沃尔什变换
参考资料
题目汇总
- bzoj4589 Hard Nim
设每堆石子个数是a1,a2,a3...ak,先手必胜的条件是a1 xor a2 xor a3 ... ak == 0
fwt可以计算下标求xor 等于一个定值的数的乘积的和, 因为要求所有石子堆数都是质数,所以给要卷积的数组,质数赋值为1,合数赋值为0,这样只有所有的数字都是1乘积才为1,求和后就是总的方案数 - 51nod1773 A国的贸易
每过一天,城市i的商品数就变成a[i] = a[i] + ∑a[j] (j xor i==2^m)
令b数组为b[0]=1,b[2^m]=1,其余为0
异或具有自反性质,所以 a[k] = ∑ a[i]*b[j] (i xor j = k) 当j为2^m时b才为1
这满足fwt_xor 应用条件,t天就乘t个b,用快速幂加速即可 - 牛客多校第九场A Circulant Matrix 纯裸沙比题,不放代码了
- 2018沈阳onsite I Distance Between Sweethearts 博客有题解
模板
最常见的是 xor 题目,结合自反性质构造卷积式
void fwt_xor(ll a[], int len, int on){
int inv2 = 500000004;
for (int i = 1; i < len; i <<= 1)
for (int p = i<<1, j = 0;j < len; j += p)
for (int k = 0; k < i; k++) {
int x = a[j + k], y = a[i + j + k];
a[j + k] = (x + y) % MOD;
a[j + k + i] = (x - y + MOD) % MOD;
if (on == -1) {
a[j + k] = a[j + k] * inv2 % MOD;
a[i + j + k] = a[i + j + k] * inv2 % MOD;
}
}
}
void fwt(ll a[],int n,int v) {
for(int d=1;d<n;d<<=1) {
for(int m = d<<1,i=0;i<n;i+=m) {
for(int j=0;j<d;j++){
ll x = a[i+j],y = a[i+j+d];
if(v == 1) a[i+j] = (x+y),a[i+j+d] = (x-y);
else a[i+j] = (x+y)/2,a[i+j+d] = (x-y)/2;
}
}
}
}
此外还有 or 以及 and 的代码 (未使用过)
void fwt_and(ll a[], int len, int on)
{
for(int i = 1;i < len;i <<= 1)
for(int p = i << 1, j = 0; j < len; j += p)
for(int k = 0; k < i; k++)
if(on == 1)a[j + k]=(a[j + k] + a[i + j + k]) % MOD;
else a[j + k]=(a[j + k] + MOD - a[i + j + k]) % MOD;
}
void fwt_or(ll a[], int len, int on)
{
for(int i = 1;i < len;i <<= 1)
for(int p = i << 1, j = 0; j < len; j += p)
for(int k = 0; k < i; k++)
if(on == 1)a[i + j + k]=(a[j + k] + a[i + j + k]) % MOD;
else a[i + j + k]=(a[i + j + k] + MOD - a[j + k]) % MOD;
}