原理
对于长度为 2n 的数组 Ai,Bi 的卷积定义如下:
Ck=iopj=k∑AiBj
这里的 op 可以是与、或、异或。
与卷积
我们想找到一个可逆变换 F,使得下面式子成立(无说明下面的加法和乘法都是逐位进行):
F(C)=F(A)F(B)
并且我们希望他是线性的,即:
F(A+B)=F(A)+F(B)
这样就可以快速的计算了。
我们根据最高位是 0/1 将数组 A 分为 A0,A1,同理 B,C 也是。
那么看原来的式子,由于只有 1and1=1,其它的结果都是 0,因此:
C0kC1k=iandj=k∑A0iB0j+A0iB1j+A1iB0j=iandj=k∑A1iB1j
那么,对这两个式子用 F 进行变换:
F(C0)F(C1)=F(A0)F(B0)+F(A0)F(B1)+F(A1)F(B0)=F(A1)F(B1)
两式相加可以得到:
F(C0+C1)=F(A0+A1)F(B0+B1)
所以,我们可以这样构造 F(C):
F(C)=(F(C0)+F(C1),F(C1))
就可以使得前半部分和后半部分都满足:
F(C)=F(A)F(B)
那么,我们还需要研究一下这个变换的逆变换,这里上标 * 表示当前这个数组是变换后的数组:
F−1(C∗)=(F−1(C0∗)−F−1(C1∗),F−1(C1∗))
所以我们构造出了一种快速求与卷积的方式,即先进行 F 变换,然后逐位相乘,最后通过逆变换 F−1 复原。
或卷积
同样的分类方法,我们可以将所有的数组分成两部分,然后:
C0kC1k=iorj=k∑A0iB0j=iorj=k∑A0iB1j+A1iB0j+A1iB1j
设满足要求的变换是 G,那么两边取 G 可以得到:
G(C0)G(C1)=G(A0)G(B0)=G(A0)G(B1)+G(A1)G(B0)+G(A1)G(B1)
同上,可以构造出这样的变换:
G(C)=(G(C0),G(C0)+G(C1))
逆变换是:
G−1(C∗)=(G−1(C0∗),G−1(C1∗)−G−1(C0∗))
异或卷积
分类方法同上:
C0kC1k=i⊕j=k∑A0iB0j+A1iB1j=i⊕j=k∑A0iB1j+A1iB0j
设满足要求的变换是 H,那么可以得到:
H(C0)=H(A0)H(B0)+H(A1)H(B1)H(C1)=H(A1)H(B0)+H(A0)H(B1)
两式相加相减:
H(C0+C1)=H(A0+A1)H(B0+B1)H(C0−C1)=H(A0−A1)H(B0−B1)
构造变换:
H(C)=(H(C0)+H(C1),H(C0)−H(C1))
因此逆变换为:
H−1(C∗)=21(H−1(C0∗)+H−1(C1∗),H−1(C0∗)−H−1(C1∗))
对于上面的所有变换,最终数组长度为 1 时,保留其为那个数字本身即可,因为这三个卷积对于只有一个数字的情况都是普通的乘法。
Binary Table
给定 n≤20 行 m≤105 列表格,每个元素是 0/1,每次操作选择一行或一列翻转,问经过若干次操作后表格中最少有多少个 1。
首先,翻转等价于 ⊕1。而异或具有交换率,所以我们可以认为先进行行操作,最后统一进行列操作。
将第 i 列看作一个二进制数 vi,当行操作结束时,相当于给每一列选择了一个 0≤x≤2n−1 使得 vi←vi⊕x。
由于最后进行列操作,所以每一列的贡献 fv=min{popcountv,n−popcountv}。
枚举所有的 x,最后的结果就是:
x=0min2n−1{i=1∑mfvi⊕x}
一个常见的套路是将枚举每一个 vi 转化为枚举 vi 的个数,设 gv=∑i=1m[vi=v],那么目标式转化为:
x=0min2n−1{v=0∑2n−1fv⊕xgv}=x=0min2n−1{i⊕j=x∑figj}
因此求一个 f 与 g 的异或卷积求参数的最小值就是答案。
Little Pony and Elements of Harmony
给定 m≤20,t≤1018,p≤109,令 n=2m,给定长度为 n 的数组 e0[−] 和长度为 m+1 的数组 b0∼bm,定义 ei 与 ei−1 的递推式:
ei[u]=v∑ei−1[v]b[popcount(u⊕v)]
求 et[−]modp。注意 p 不保证是一个质数。
显然,我们令 f[x]=b[popcountx],就会有:
ei[u]=v∑ei−1[v]f[u⊕v]=x⊕y=u∑ei−1[x]f[y]
那么 ei=ei−1∗f,则 et=e0∗ft。两边取一个 FWT 就会得到:
F(et)=F(e0)[F(f)t]
这里的次方是逐位求次方。
还有一个问题,就是 p 不一定是一个质数,那么 2 的逆元就不一定存在。这里修改一下逆变换的过程,设答案数组是 a,如果逆变换过程中不进行除 2 的操作,那么最终得到的数组就会是 2mai。只需要将模数预先乘 2m,设我们得到的结果数组是 x,就会有:
2maiai≡xi(mod2mp)≡2mxi(modp)
这里 2mp 比较大,乘法需要用到 int128。