Skip to content

FWT 笔记 ​

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

C(k)=∑i⊕j=kA(i)B(j)

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

0. 核心思想 ​

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

1. 按位或 ​

构造

FA(x)=∑iorx=xA(i)

则

FA(x)×FB(x)=∑iorx=xA(i)∑jorx=xB(j)=∑iorx=x∑jorx=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,k都是1的位置))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)=0∧f(j)=0A(i)B(j)−∑f(i)=0∧f(j)=1A(i)B(j)−∑f(i)=1∧f(j)=0A(i)B(j)+∑f(i)=1∧f(j)=1A(i)B(j)=∑f(ixorj)=0A(i)B(j)−∑f(ixorj)=1A(i)B(j)=FC(x)

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

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

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

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

综上,L′=L+R,R′=L−R。

考虑从 FA→A,则有 L=L′+R′2,R=L′−R′2。

点击查看代码
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;
            }
}
最近更新