FWT

快速沃尔什变换

      • 算法流程
      • P4717 【模板】快速沃尔什变换 (FWT)

算法流程

(1) 计算 c i = ∑ i = j ∣ k a j b k c_i=\sum_{i=j|k} a_jb_k ci=i=jkajbk

f w t [ a ] i = ∑ j ∣ i = i a j fwt[a]_i=\sum_{j|i=i} a_j fwt[a]i=ji=iaj,设序列左半边为 a 0 a_0 a0,右半边为 a 1 a_1 a1,可以得到:
f w t [ a ] = m e r g e r ( f w t [ a 0 ] , f w t [ a 0 ] + f w t [ a 1 ] ) fwt[a]=merger(fwt[a_0],fwt[a_0]+fwt[a_1]) fwt[a]=merger(fwt[a0],fwt[a0]+fwt[a1])

即,取 i = 0 i = 0 i=0 时, j = 0 j =0 j=0,满足 j ∣ i = i j | i = i ji=i 。取 i = 1 i = 1 i=1 时, j = 0 j =0 j=0 或者 1 1 1

那么从 f w t [ a ] fwt[a] fwt[a] 变为 a a a 就相当于求逆
a = m e r g e r ( a 0 , a 1 − a 0 ) a=merger(a_0,a_1-a_0) a=merger(a0,a1a0)

(2) 计算 c i = ∑ i = j & k a j b k c_i=\sum_{i=j\&k} a_jb_k ci=i=j&kajbk

f w t [ a ] i = ∑ j & i = i a j fwt[a]_i=\sum_{j\&i=i} a_j fwt[a]i=j&i=iaj,设序列左半边为 a 0 a_0 a0,右半边为 a 1 a_1 a1,可以得到:
f w t [ a ] = m e r g e r ( f w t [ a 0 ] + f w t [ a 1 ] , f w t [ a 1 ] ) fwt[a]=merger(fwt[a_0]+fwt[a_1],fwt[a_1]) fwt[a]=merger(fwt[a0]+fwt[a1],fwt[a1])

即,取 i = 0 i = 0 i=0 时, j = 0 j =0 j=0 或者 1 1 1,满足 j & i = i j \& i = i j&i=i 。取 i = 1 i = 1 i=1 时, j = 0 j =0 j=0

那么从 f w t [ a ] fwt[a] fwt[a] 变为 a a a 就相当于求逆
a = m e r g e r ( a 0 − a 1 , a 1 ) a=merger(a_0-a_1,a_1) a=merger(a0a1,a1)

(3) 计算 c i = ∑ i = j ⊕ k a j b k c_i=\sum_{i=j \oplus k} a_jb_k ci=i=jkajbk

f w t [ a ] = m e r g e r ( f w t [ a 0 ] + f w t [ a 1 ] , f w t [ a 0 ] − f w t [ a 1 ] ) fwt[a]=merger(fwt[a_0]+fwt[a_1],fwt[a_0]-fwt[a_1]) fwt[a]=merger(fwt[a0]+fwt[a1],fwt[a0]fwt[a1])

a = m e r g e r ( a 0 + a 1 2 , a 0 − a 1 2 ) a=merger(\frac {a_0+a_1}2,\frac {a_0-a_1}2) a=merger(2a0+a1,2a0a1)

参考链接

习题:https://www.luogu.com.cn/problem/CF662C

P4717 【模板】快速沃尔什变换 (FWT)

链接:https://www.luogu.com.cn/problem/P4717

FWT_第1张图片

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=150000,mod=998244353,inv2=499122177;

int n,total;
int a[maxn],b[maxn],A[maxn],B[maxn];

void add(int &x,int y)
{
    x+=y;
    if(x>=mod) x-=mod;
    if(x<0) x+=mod;
}

void fwt_or(int *A,int f)
{
    for(int mid=1,len=2;mid<total;mid<<=1,len<<=1)
        for(int p=0;p<total;p+=len)
            for(int k=0;k<mid;++k)
                add(A[p+mid+k],A[p+k]*f);
}
void fwt_and(int *A,int f)
{
    for(int mid=1,len=2;mid<total;mid<<=1,len<<=1)
        for(int p=0;p<total;p+=len)
            for(int k=0;k<mid;++k)
                add(A[p+k],A[p+mid+k]*f);
}
void fwt_xor(int *A,int f)
{
    for(int mid=1,len=2;mid<total;mid<<=1,len<<=1)
    {
        for(int p=0;p<total;p+=len)
        {
            for(int k=0;k<mid;++k)
            {
                int x=A[p+k],y=A[p+mid+k];
                A[p+k]=(1ll*x+y)%mod;
                A[p+mid+k]=(1ll*x-y+mod)%mod;
                if(f==-1)
                {
                    A[p+k]=1ll*A[p+k]*inv2%mod;
                    A[p+mid+k]=1ll*A[p+mid+k]*inv2%mod;
                }
            }
        }
    }
}
void init()
{
    for(int i=0;i<total;++i)
        a[i]=A[i],b[i]=B[i];
}
void print()
{
    for(int i=0;i<total;++i)
        printf("%d%c",a[i],i==total-1?'\n':' ');
}
void solve1()
{
    init();
    fwt_or(a,1),fwt_or(b,1);
    for(int i=0;i<total;++i) a[i]=1ll*a[i]*b[i]%mod;
    fwt_or(a,-1);
    print();
}
void solve2()
{
    init();
    fwt_and(a,1),fwt_and(b,1);
    for(int i=0;i<total;++i) a[i]=1ll*a[i]*b[i]%mod;
    fwt_and(a,-1);
    print();
}
void solve3()
{
    init();
    fwt_xor(a,1),fwt_xor(b,1);
    for(int i=0;i<total;++i) a[i]=1ll*a[i]*b[i]%mod;
    fwt_xor(a,-1);
    print();
}
int main()
{
    scanf("%d",&n);
    total=1<<n;
    for(int i=0;i<total;++i) scanf("%d",&A[i]);
    for(int i=0;i<total;++i) scanf("%d",&B[i]);
    solve1();
    solve2();
    solve3();
    return 0;
}

你可能感兴趣的:(多项式,数学,FWT)