fwt小记

这是属于补锅计划的一部分。

那就直接进入正题。

理论基础

用于解决求解 AB 的方法,其中( 是二进制运算)

名字很高级 快速莫比乌斯/沃尔什变换 (FMT/FWT) ,其实基本思想比较简单。

假设有一个序列 A ,通过某种变换我们可以得到另一个序列 fwt[A] ,如果对于整个序列 A 复杂度为 O(n2) 的计算可以等价于在 fwt[A] 中的 O(n) 的计算,那么只要这种变换的复杂度很低,那么整个过程的复杂度就降下来了。

比如卷积,两个函数的的卷积我们不好求,但我们可以用 O(nlogn) 的复杂度求出他的点值,而点值的运算等价于原函数的计算,那么整个FFT的复杂度就是 O(nlogn) 的。

具体去解释这个变换比较复杂,推荐先通过模板题题解区了解具体操作再来学习理论。

这里就挑其中的一些重点,省去了一些证明,全面的解释见

  • FWT 是一个线性变换 ,即你可以理解为 A 是一个 n 维向量,变换是一个 n×n 的矩阵 Cfwt[A]i=j=0n1AjCi,j

  • 因为 是二进制运算,所以我们可以把每一位分开考虑(按位拆半)。

    A0 为幂级数下标首位为 0 的部分,类似地有 A1

    • i0=0 ,则有 :

      fwt[A]i=c(0,0)fwt[A0]i+c(0,1)fwt[A1]i(i[0,n/2))

    • i0=1 ,则有 :

      fwt[A]i+(n/2)=c(1,0)fwt[A0]i+c(1,1)fwt[A1]i(i[0,n/2))

    所以对于 C 矩阵来说,有用的只有 c([0,1],[0,1])

  • fwt[A] 变换回 A 就是通过 C 的逆矩阵得到,因为 AC=fwt[A]fwt[A]C1=A

模板

即求解 ci=j|k=iajbk

  • 矩阵: [1011]

    fwt[A]i=fwt[A0]ifwt[A]i+(n/2)=fwt[A0]i+fwt[A1]i

    c(i,j)=[i|j=i]

    含义:子集求和(高维前缀和)

  • 逆矩阵: [1011]

    fwt[A]i=fwt[A0]ifwt[A]i+(n/2)=fwt[A0]i+fwt[A1]i

    c(i,j)=[i|j=i]

    含义:高维前缀差。

即求解 ci=j&k=iajbk

  • 矩阵: [1101]

    fwt[A]i=fwt[A0]i+fwt[A1]ifwt[A]i+(n/2)=fwt[A1]i

    c(i,j)=[i&j=i]

    含义:超集求和(高维后缀和)

  • 逆矩阵: [1101]

    fwt[A]i=fwt[A0]ifwt[A1]ifwt[A]i+(n/2)=fwt[A1]i

    c(i,j)=[i&j=i]

    含义:高维后缀差。

异或

即求解 ci=jxork=iajbk

  • 矩阵: [1111]

    fwt[A]i=fwt[A0]i+fwt[A1]ifwt[A]i+(n/2)=fwt[A1]ifwt[A1]i

    c(i,j)=(1)|i&j|

    含义:不知道...

  • 逆矩阵: [0.50.50.50.5]

    fwt[A]i=12(fwt[A0]i+fwt[A1]i)fwt[A]i+(n/2)=12(fwt[A1]ifwt[A1]i)

    c(i,j)=12n(1)|i&j|

代码,复杂度是 O(nlogn) 的。

例题

因为 n 很小,我们可以暴力考虑每一行翻不翻转。

对应的看列状态,设 ai 表示 i 这种状态的出现次数,bi 表示二进制数 i0/1 较小值。

那么当枚举的行翻转状态为 x ,那么 ans=i=02naibixorx

变一下就有 ans=ixorj=xaibj ,那么我们就可以用 xor 卷积一次求出所有行翻转状态的答案,最后取 min 就是答案。

如果没有 i&j=0 这是好做的。

注意到 i|j=s,i&j=0 等价于 i|j=s,|i|+|j|=|s|

那么我们可以把单个的值换成是一个多项式,把上面提到的加法、乘法都转换成多项式的加法、乘法,之后就是一模一样的了。

复杂度是 O(nlog2n) 的。

实现上可以把多项式维度转移到前面,可以结合代码理解。

还有一个形如 S[s]=C[s]tsS[t]W[st] 这样的dp也可以通过子集卷积来优化。

题目:P4221 [WC2018]州区划分

没有什么特别重要的,就直接给题解链接了

还有一些板子,就不说了。

高级运用

复杂理论的用处来了。

因为这题是要求一堆数与起来为 0 ,和我们上面所处理的两个数异或起来不同,这很像几个多项式乘在一起。

那么我们就可以构造多项式 Fi,其中 Fi[m]=Fi[ai]=1,m=2n1 这样在 于卷积 意义下分别表示 不选、选 ai

那么答案就是 F(x)=[x0]iFi(x) ,但暴力乘起来的复杂度是 O(n2logn) 的。

每个多项式都只有两个非 0 项,我们来寻找一下规律:

c(i,j) 为and卷积的变换系数,则 fwt[Fk]i=j=0mc(i,j)Fk[j]=c(i,m)+c(i,ak)

经过我们上面的分析,c(i,j)=[i&j=i] ,那么我们就知道:

fwt[Fk]i={2i&ak=i1i&aki

因为 fwt[F(x)]i=k=1nfwt[Fk]i ,那么我们只要知道第 i 位有多少个 2 ,那么 fwt[F(x)]i 就等于 2 的多少次方。相当于对每个 i 求有多少个 ak&i=i ,就是或卷积。

求出来之后直接再逆操作转成 F(x) 即可。

仿照上题,我们设 Fk(x) ,其中 Fk[ak]=x,Fk[bk]=y,Fk[ck]=z ,答案为 [xi]Fk(x)

同样 fwt[Fk]i=c(i,ak)x+c(i,bk)y+c(i,ck)z ,然后有 c(i,j)=(1)|i&j|

那么 fwt[F(x)]i=k=1nfwt[Fk]i=k=1n(1)|i&ak|x+(1)|i&bk|y+(1)|i&ck|z

到了这里,发现我们无法适用上面的解法了,因为后面的式子比较复杂。

但是因为 (1)|i&ak|x+(1)|i&bk|y+(1)|i&ck|z 只有 8 种取值,如果我们分别得到其个数,就可以用快速幂得到答案。

8 种还是太多了,有一种巧妙的化简方法 :

由异或的自反性,每个把三元组 {ai,bi,ci} 异或上 ai ,变为 {0,biai,ciai}

那么最后的结果异或上 i=1nai 就能得到原来的答案。

所以就变成了 4 种。

对于 i 来说,我们设 c1,c2,c3,c4 分别表示 x+y+z,x+yz,xy+z,xyz 的个数。

那么 c1=k=1n[(1)|i&bk|=1(1)|i&ck|=1] ,同样可以得到 c2,c3,c4 的含义。

发现这和异或情况下的 c(i,j) 的系数很相似。

我们可以令 Fk[bk]=1 ,即 x=0,y=1,z=0 ,那么此时 fwt[Fk]i=(1)|i&bk| ,于是 k=1nfwt[Fk]i=p1=c1+c2c3c4

怎么求 k=1nfwt[Fk]i ,我们有 fwt(F+G)=fwt(F)+fwt(G) ,因为这东西是一个线性变换,其实就是向量乘矩阵 ,那么我们计算 fwt(k=1nFk) 就可以了。

同样的,还有 令 Fk[ck]=1 ,此时 k=1nfwt[Fk]i=p1=c1c2+c3c4

Fk[bk]=Fk[ck]=1 ,此时 k=1nfwt[Fk]i=p1=c1c2c3+c4

还有 c1+c2+c3+c4=n ,那么就可以解方程解出 c1,c2,c3,c4 的值了。

最后像上题一样逆操作转成 F(x) 即可。

和多项式高级操作的结合

没想到吧,这玩意还可以求逆、ln、exp,小编也没想到,所以这部分就咕咕咕了。

到时候再补吧

posted @   qwq_123  阅读(72)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示