快速沃尔什变换 [学习笔记]

快速沃尔什变换

概述

用来解决一类与位运算有关卷积问题:

\[C_i = \sum_{j \oplus k = i}A_j * B_K \]


过程

具体看picks的博客和这两篇blog吧:1 2

基础思想和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})) \\ \]


代码:

4589: Hard Nim

#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]);
	}
}

posted @ 2017-04-26 23:22  Candy?  阅读(701)  评论(0编辑  收藏  举报