FWT学习笔记

FWT学习笔记


参考:

  1. 快速沃尔什变换(FWT)学习笔记
  2. FWT 详解 知识点

定义:

快速沃尔什变换(FWT)主要解决位运算卷积的问题。给定两个数组 \(A\)\(B\) (长度为2的整数幂):
\[C_k = \sum_{i \oplus j=k}A_i·B_i\] 其中\(\oplus\)可以是与,或,异或。FWT利用类似于FFT的想法,把 \(A\)\(B\) 分别正变换,在一个好的复杂度得到 \(A\)\(B\) 按位卷积的的正变换,最后再逆变换回来就是答案。

模版[Luogu4717]

#include <bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;++i)
typedef long long ll;
const int mod = 998244353;
const int inv2 = 499122177;
const int N = (1<<17);
inline int read() {
    char c=getchar(); int x=0,f=1;
    while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
    while(isdigit(c)){x=x*10+c-'0';c=getchar();}
    return x*f;
}
inline void write(int x) {
    if(x>=10)write(x/10);
    putchar('0'+x%10);
}
using namespace std;
inline int add(int x,int y) {
    x+=y;
    if(x>=mod)x-=mod;return x;
}
inline int sub(int x,int y) {
    x-=y;
    if(x<0)x+=mod;return x;
}
void FWT_or(int a[],int n,int on) {
    for(int i=1;i<n;i<<=1) {
        for(int j=0;j<n;j+=(i<<1)) {
            for(int k=0;k<i;++k) {
                int u=a[j+k], t=a[j+k+i];
                a[j+k]=u;
                if(on==1)a[j+k+i]=add(t,u);
                else a[j+k+i]=sub(t,u);
            }
        }
    }
}
void FWT_and(int a[],int n,int on) {
    for(int i=1;i<n;i<<=1) {
        for(int j=0;j<n;j+=(i<<1)) {
            for(int k=0;k<i;++k) {
                int u = a[j+k], t=a[j+k+i];
                if(on==1) a[j+k]=add(u,t);
                else a[j+k]=sub(u,t);
                a[j+k+i]=t;
            }
        }
    }
}
void FWT_xor(int a[],int n,int on) {
    for(int i=1;i<n;i<<=1) {
        for(int j=0;j<n;j+=(i<<1)) {
            for(int k=0;k<i;++k) {
                int u=a[j+k], t=a[j+k+i];
                a[j+k]=add(u,t); a[j+k+i]=sub(u,t);
                if(on==-1) {
                    a[j+k]=(ll)a[j+k]*inv2%mod;
                    a[j+k+i]=(ll)a[j+k+i]*inv2%mod;
                }
            }
        }
    }
}
int a[N],b[N],c[N],d[N];
int main() {
    int n = read(), m = (1<<n);
    rep(i,0,m-1) a[i]=read();
    rep(i,0,m-1) b[i]=read();

    memcpy(c,a,sizeof(c));
    memcpy(d,b,sizeof(b));
    FWT_or(c,m,1); FWT_or(d,m,1);
    rep(i,0,m-1) c[i]=(ll)c[i]*d[i]%mod;
    FWT_or(c,m,-1);
    rep(i,0,m-1)write(c[i]),putchar(' ');
    putchar('\n');

    memcpy(c,a,sizeof(c));
    memcpy(d,b,sizeof(b));
    FWT_and(c,m,1); FWT_and(d,m,1);
    rep(i,0,m-1) c[i]=(ll)c[i]*d[i]%mod;
    FWT_and(c,m,-1);
    rep(i,0,m-1)write(c[i]),putchar(' ');
    putchar('\n');

    memcpy(c,a,sizeof(c));
    memcpy(d,b,sizeof(b));
    FWT_xor(c,m,1); FWT_xor(d,m,1);
    rep(i,0,m-1) c[i]=(ll)c[i]*d[i]%mod;
    FWT_xor(c,m,-1);
    rep(i,0,m-1)write(c[i]),putchar(' ');
    putchar('\n');
    
    return 0;
}

BZOJ4589[Hard Nim]

题意:求长度为n,元素为小于m的质数,且异或和为0的方案数

做法:设 \(f[i][j]\) 表示长度为i的序列异或和为j的方案数,则\(f[i][x \oplus y] = f[i-1][x]·f[1][y]\)\(f[1]\)内编号为素数的位置为1,其余为0,那么\(f[i][k] = \sum _{x \oplus y=k} f[i-1][x]·f[1][y]\),答案就是要求 \(f[1]\) 卷积 \(n\) 次的f[n][0],又因为每次乘的都是 \(f[1]\) 所以可以先对 \(f[1]\) 求FWT正变换,分别n次幂后再加起来,最后逆变换出答案向量 \(f[n]\)

#include <bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;++i)
#define per(i,a,b) for(int i=a;i>=b;--i)
#define pb push_back
typedef long long ll;
const int N = 65537<<1;
const ll mod = 1e9 + 7;
using namespace std;
int m, p[N], notp[N];
ll n,a[N],inv2;
ll q_pow(ll a,ll b) {
    ll ans = 1;
    while(b) {
        if(b&1) ans=(ans*a)%mod;
        a=(a*a)%mod;
        b>>=1;
    }
    return ans;
}
inline ll add(ll x,ll y) {
    x+=y;
    if(x>=mod)x-=mod;
    return x;
}
inline ll sub(ll x,ll y) {
    x-=y;
    if(x<0)x+=mod;
    return x;
}
void FWT_xor(ll a[],int n,int on) {
    for(int i=1;i<n;i<<=1) {
        for(int j=0;j<n;j+=(i<<1)) {
            for(int k=0;k<i;++k) {
                ll u = a[j+k], t = a[j+k+i];
                a[j+k]=add(u,t); a[j+k+i]=sub(u,t);
                if(on==-1) {
                    a[j+k]=a[j+k]*inv2%mod;
                    a[j+k+i]=a[j+k+i]*inv2%mod;
                }
            }
        }
    }
}
void init() {
    inv2 = q_pow(2,mod-2);
    notp[1] = 1;
    for(int i=2;i<=5e4;++i) {
        if(!notp[i])p[++p[0]]=i;
        for(int j=1;j<=p[0]&&p[j]*i<=5e4;++j) {
            notp[p[j]*i] = 1;
            if(i%p[j]==0)break;
        }
    }
}
int main() {
    init();
    while(~scanf("%lld%d",&n,&m)) {
        memset(a,0,sizeof(a));
        rep(i,1,m) a[i] = (!notp[i]);
        int len;
        for(len=1;len<=m;len<<=1);
        FWT_xor(a,len,1);
        rep(i,0,len-1) a[i]=q_pow(a[i],n);
        FWT_xor(a,len,-1);
        printf("%lld\n",a[0]%mod);
    }
    return 0;
}

转载于:https://www.cnblogs.com/RRRR-wys/p/9478045.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值