Skip to content
0

FWT 笔记

用于给定 A(x),B(x),解决计算

C(k)=ij=kA(i)B(j)

的问题。其中 是按位或,按位与或按位异或。

0. 核心思想

构造多项式 FA,使得 A 与其可以在 O(nlogn) 的时间内相互转换,并且 FA×FB 可以在 O(n) 内计算。

1. 按位或

构造

FA(x)=iorx=xA(i)

FA(x)×FB(x)=iorx=xA(i)jorx=xB(j)=iorx=xjorx=xA(i)B(j)=(iorj)orx=xA(i)B(j)=FC(x)

考虑如何将 A 转换为 FA,将 A 的长度补到二的次幂后,可以进行分治,可以发现左边的数的最高位全为 0,右边的数的最高位全为 1

除最高位外,左边的第 i 个数和右边的第 i 个数相同,因此,每一个左边的第 i 个数都对右边的第 i 个数有贡献。

按层自下向上递推,不断增加对右侧贡献的间隔长度即可。

考虑将 FA 转换为 A,分治后,右边的第 i 个数要减去左边的第 i 个数的贡献,因此这两个东西本质相同,一个函数即可解决。

点击查看代码
cpp
// 当前分治区间长度为 p,左端点为 i,半个区间的长度为 q
// 左边第 j 个数向右边第 j 个数贡献(i+j -> i+j+p)
void OR(int *f, int opt) {
    for (int p = 2, q = 1; p <= 1 << n; p <<= 1, q <<= 1)
        for (int i = 0; i < 1 << n; i += p)
            for (int j = 0; j < q; j++)
                f[i + j + q] = (f[i + j + q] + f[i + j] * opt + mod) % mod;
}

2. 按位与

和按位或同理,只是变成右边向左边贡献了。

点击查看代码
cpp
void AND(int *f, int opt) {
    for (int p = 2, q = 1; p <= 1 << n; p <<= 1, q <<= 1)
        for (int i = 0; i < 1 << n; i += p)
            for (int j = 0; j < q; j++)
                f[i + j] = (f[i + j] + f[i + j + q] * opt + mod) % mod;
}

3. 按位异或

构造

FA(x)=p(iandx)mod2=0A(i)p(iandx)mod2=1A(i)

其中 p(x) 表示 popcount(x)

可以发现

(p(iandj)mod2)xor(p(iandk)mod2)=(p(iandj)+p(iandk))mod2p(iand(jxork))mod2=(p(iandj)+p(iandk)2(i,j,k1))mod2

后面那个 2 的倍数 mod2 变成 0 了。

f(i)=p(iandx)mod2,故

f(i)xorf(j)=f(ixorj)

因此

FA(x)×FB(x)=(f(i)=0A(i)f(i)=1A(i))(f(j)=0B(j)f(j)=1B(j))=f(i)=0f(j)=0A(i)B(j)f(i)=0f(j)=1A(i)B(j)f(i)=1f(j)=0A(i)B(j)+f(i)=1f(j)=1A(i)B(j)=f(ixorj)=0A(i)B(j)f(ixorj)=1A(i)B(j)=FC(x)

考虑 AFA 同样分治,左边第 i 个数 L 和右边第 i 个数 R,二进制 1 的个数奇偶必然不同。

首先考虑这个 and,必然同 2 有右边向左边贡献。

接着考虑右边,因为全都多了一位,所以他们内部之间的 p(iandj)mod2 必然发生了改变,由于恰好是减号,所以直接取反。

由于只关心 mod2 的值,所以左边也对右边有贡献。

综上,L=L+R,R=LR

考虑从 FAA,则有 L=L+R2,R=LR2

点击查看代码
cpp
void XOR(int *f, int opt) {
    for (int p = 2, q = 1; p <= 1 << n; p <<= 1, q <<= 1)
        for (int i = 0; i < 1 << n; i += p)
            for (int j = 0; j < q; j++) {
                int l = f[i + j], r = f[i + j + q];
                f[i + j] = (l + r) % mod * opt % mod;
                f[i + j + q] = (l - r + mod) % mod * opt % mod;
            }
}
最近更新