快速沃尔什变换 [学习笔记]
快速沃尔什变换
概述
用来解决一类与位运算有关卷积问题:
\[C_i = \sum_{j \oplus k = i}A_j * B_K
\]
过程
基础思想和fft类似,我们正变换求出一个类似点值表示的东西,然后用它直接乘,然后逆变换。
fft我们对下标奇偶分治。这里求变换我们按位分治。
与和或根据位运算的性质很好想,可以自己推一推异或记住就行了
二进制运算的性质很强啊,重点在于没有进位!所以说类似快速幂可以直接对“点值表示”做.
与
\[A=(A_0,A_1) \\
fwt(A) = (fwt(A_0)+fwt(A_1),\ fwt(A_1)) \\
ifwt(A) = (ifwt(A_0)-ifwt(A_1),\ ifwt(A_1)) \\
\]
或
\[A=(A_0,A_1) \\
fwt(A) = (fwt(A_0),\ fwt(A_1) + fwt(A_0)) \\
ifwt(A) = (ifwt(A_0),\ ifwt(A_1)- ifwt(A_0)) \\
\]
异或
\[A=(A_0,A_1) \\
fwt(A) = (fwt(A_0)+fwt(A_1),\ fwt(A_0)-fwt(A_1)) \\
ifwt(A) = (ifwt(\frac{A_0+A_1}{2}),\ ifwt(\frac{A_0-A_1}{2})) \\
\]
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
typedef long long ll;
const int N = (1<<17)+5, P = 1e9+7, inv2 = (P+1)/2;
bool notp[N]; int p[N/10];
void sieve(int n) {
for(int i=2; i<=n; i++) {
if(!notp[i]) p[++p[0]] = i;
for(int j=1; j<=p[0] && i*p[j]<=n; j++) {
notp[i*p[j]] = 1;
if(i%p[j] == 0) break;
}
}
}
void fwt(int *a, int n, int flag) {
for(int l=2; l<=n; l<<=1) {
int m = l>>1;
for(int *p = a; p != a+n; p += l)
for(int k=0; k<m; k++) {
int x = p[k], y = p[k+m];
if(flag == 1) p[k] = (x + y) %P, p[k+m] = (x - y + P) %P;
else p[k] = (ll) (x + y) * inv2 %P, p[k+m] = (ll) (x - y + P) * inv2 %P;
}
}
}
int Pow(ll a, int b) {
ll ans = 1;
for(; b; b>>=1, a=a*a%P)
if(b&1) ans=ans*a%P;
return ans;
}
int n, m, a[N];
int main() {
freopen("in", "r", stdin);
sieve(N-1);
while(scanf("%d %d", &n,&m) != EOF) {
memset(a, 0, sizeof(a));
for(int i=1; i<=p[0] && p[i]<=m; i++) a[p[i]] = 1;
int len = 1; while(len <= m) len<<=1;
fwt(a, len, 1);
for(int i=0; i<len; i++) a[i] = Pow(a[i], n);
fwt(a, len, -1);
printf("%d\n", a[0]);
}
}
Copyright:http://www.cnblogs.com/candy99/