FWT 学习笔记

FFTNTT 都是在做加法卷积,从其形式 Ci=j=0iAj×Bij 可以看出,其满足对应的下标 j+(ij)=i

考虑把这个 + 换成位运算 bitand,bitor,bitxor 来卷积。
即令这个符号为 ,有 Ci=jk=iAj×Bk(i,j,k[0,n),n=2m)

考虑类似 FFT 的过程:
ADFTFFT(A),BDFTFFT(B),FFT(A)i×FFT(B)iFFT(C)i,FFT(C)IDFTC
那能不能也这样通过 DWT 得到 FWT(A),FWT(B),然后得到 FWT(C),通过 IDWT 得到 C

考虑构造系数 c(i,j) 满足 FWT(A)i=c(i,j)×Aj

那么根据 FWT(A)i×FWT(B)i=FWT(C)i 就有:
(jc(i,j)Aj)(kc(i,j)Bk)=pc(i,p)Cp
jkc(i,j)c(i,k)AjBk=pc(i,p)Cp

考虑卷积的形式,有 Cp=p1p2=pAp1×Bp2,就有:
pCp=pp1p2=pAp1Bp2
pc(i,p)Cp=pc(i,p)p1p2=pAp1Bp2
pc(i,p)Cp=pp1p2=pc(i,p1p2)Ap1Bp2
pc(i,p)Cp=p1p2c(i,p1p2)Ap1Bp2

把两个最终的式子放在一起,就有 jkc(i,j)c(i,k)AjBk=p1p2c(i,p1p2)Ap1Bp2
就能得到 c(i,j)c(i,k)=c(i,jk)

又根据位运算每一位是独立的这一性质,考虑把 c(i,j) 摊到 (i,j) 的每一位上。
即令 ipi 二进制下的第 p 高位(p=0 为最高位),考虑得到了 c(i,j)(i,j[0,1]),有 c(i,j)=pc(ip,jp)
只要有 c(ip,jp)c(ip,kp)=c(ip,jpkp),那么就有 c(i,j)c(i,k)=c(i,jk),通过拆位证明。

如果知道了 c(i,j),又如何进行 DWT 呢?
考虑 FWT(A)i=jn1c(i,j)Aj,那么对于最高位,在 0n21 时为 0n2n1 时为 0
i,j 分别为 i,j 舍弃最高位后的数,那就有 FWT(A)i=j=0n21c(i0,0)c(i,j)Aj+j=n2n1c(i0,1)c(i,j)Aj=c(i0,0)j=0n21c(i,j)Aj+c(i0,1)j=n2n1c(i,j)Aj
发现这也是可以分治的,考虑 A0 为首位为 0Ai 得到的序列,A1 同理。
那就有 FWT(A)i=c(i0,0)FWT(A0)i+c(i0,1)FWT(A1)i
再分讨一下 i0 的值,就有 FWT(A)i=c(0,0)FWT(A0)i+c(0,1)FWT(A1)i,FWT(A)i+n2=c(1,0)FWT(A0)i+c(1,1)FWT(A1)i(i[0,n2))

按照这个系数直接分治就行了。
因为只会往下分治 logn 层,每一层都会重新计算 FWT(A)i,时间复杂度 O(nlogn)

考虑把 c(i,j)(i,j[0,1]) 写为一个 2×2 的矩阵。
那么 IDWT 只需要把 DWTc 替换为其逆矩阵即 c1 即可。

bitor 卷积:
需要满足 c(i,j)c(i,k)=c(i,jbitork)
就有 c(i,0)c(i,0)=c(i,0),c(i,0)c(i,1)=c(i,1),c(i,1)c(i,1)=c(i,1)
可以构造出 c=[1110],c1=[0111]

bitand 卷积:
需要满足 c(i,j)c(i,k)=c(i,jbitandk)
就有 c(i,0)c(i,0)=c(i,0),c(i,0)c(i,1)=c(i,0),c(i,1)c(i,1)=c(i,1)
可以构造出 c=[1101],c1=[1101]

bitxor 卷积:
需要满足 c(i,j)c(i,k)=c(i,jbitxork)
就有 c(i,0)c(i,0)=c(i,0),c(i,0)c(i,1)=c(i,1),c(i,1)c(i,1)=c(i,0)
可以构造出 c=[1111],c1=[12121212]

模板题 Luogu P4717
直接套用上面的做法即可。
可以直接从低位到高位递推减小常数。
此题 n 为位数,时间复杂度就为 O(n2n)

#include<bits/stdc++.h>
using i64 = long long;
const i64 mod = 998244353, inv2 = (mod + 1) >> 1;
const int maxn = (1 << 17) + 2;
namespace FWT {
    const i64 Bitor[2][2] = {{1, 1}, {1, 0}}, IBitor[2][2] = {{0, 1}, {1, mod - 1}};
    const i64 Bitand[2][2] = {{1, 1}, {0, 1}}, IBitand[2][2] = {{1, mod - 1}, {0, 1}};
    const i64 Bitxor[2][2] = {{1, 1}, {1, mod - 1}}, IBitxor[2][2] = {{inv2, inv2}, {inv2, mod - inv2}};
    inline void FWT(i64 *a, const i64 B[2][2], int n) {
        for (int i = 0; (1 << i) < n; i++)
            for (int s = 0; s < n; s += 2 << i)
                for (int p = s, ed = s + (1 << i); p < ed; p++) {
                    i64 x = a[p], y = a[p + (1 << i)];
                    a[p] = (B[0][0] * x + B[0][1] * y) % mod, a[p + (1 << i)] = (B[1][0] * x + B[1][1] * y) % mod;
                }
    }
};
i64 a[maxn], b[maxn], a_[maxn], b_[maxn];
int main() {
    int n; scanf("%d", &n);
    n = 1 << n;
    for (int i = 0; i < n; i++) scanf("%lld", &a[i]);
    for (int i = 0; i < n; i++) scanf("%lld", &b[i]);
    auto solve = [&](int id) {
        for (int i = 0; i < n; i++) a_[i] = a[i], b_[i] = b[i];
        if (id == 1) FWT::FWT(a_, FWT::Bitor, n), FWT::FWT(b_, FWT::Bitor, n);
        if (id == 2) FWT::FWT(a_, FWT::Bitand, n), FWT::FWT(b_, FWT::Bitand, n);
        if (id == 3) FWT::FWT(a_, FWT::Bitxor, n), FWT::FWT(b_, FWT::Bitxor, n);
        for (int i = 0; i < n; i++) (a_[i] *= b_[i]) %= mod;
        if (id == 1) FWT::FWT(a_, FWT::IBitor, n);
        if (id == 2) FWT::FWT(a_, FWT::IBitand, n);
        if (id == 3) FWT::FWT(a_, FWT::IBitxor, n);
        for (int i = 0; i < n; i++) printf("%lld ", a_[i]); printf("\n");
    };
    solve(1),solve(2), solve(3);
    return 0;
}

本文作者:lhzawa

本文链接:https://www.cnblogs.com/lhzawa/p/18007392

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   lhzawa  阅读(42)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起