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

快速沃尔什变换

概述

用来解决一类与位运算有关卷积问题:
\[ 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]);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值