【科技】快速莫比乌斯变换(反演) 与 子集卷积

我们比较了解的是有关多项式的乘法运算,对于下标为整数,下标运算为相加等于某个数的时候,我们有很优秀的FFT做法。

但是遇到一些奇怪的卷积形式时,比如我们定义 h=fghS=LSRS[LR=S]fLgR

此时下标是一个集合,运算为集合并的卷积,我们已知了 fg ,需要快速算出 h

最暴力的做法是 O(2n) 分别枚举 LR,把答案加到 h 中去,这样复杂度是 O(4n),不太行。

这时我们就要一种高效的算法。求卷积可以用分治乘法,好像比较高妙,但我们要讲的是另一种:快速莫比乌斯变换和反演。

类比FFT,FMT也需要先把 fg 求点值,点值相乘后再插值回去,快速莫比乌斯变换就相当于点值,快速莫比乌斯反演就相当于插值。

具体证明:

  • 我们定义 f 的莫比乌斯变换为 f^ ,其中 fS^=TSfT
  • 相反的,我们定义 f^ 的莫比乌斯反演为 f,其中 fS=TS(1)|S||T|fT^,用容斥原理易得。
  • 然后我们对卷积式两边同时做莫比乌斯变换:hS^=LSRS[LRS]fLgR
  • 由于 [LRS][LS][RS],所以 hS^=LSRSfLgR
  • hS^=(LSfL)(RSgR)=fS^gS^

于是问题就在于如何快速求出 fg 莫比乌斯变换(反演)。

如果要暴力的话,可以直接枚举子集算出莫比乌斯变换(反演),这样是 O(3n),虽然比较优秀了,但复杂度还能更低。

我们考虑用递推解决:

  • fS^(i) 表示 TS[(ST){1,2,...,i}]fT
  • 易得初始状态:fS^(0)=fS
  • 对于每一个不包含 {i} 的集合 S,可知 fS^(i)=fS^(i1)(因为 S 并没有 i 这位),f^S{i}(i)=f^S(i1)+f^S{i}(i1)(前者的 T 没有包含 {i},而后者的 T 必须包含了 {i})。
  • 显然,递推了 n 轮之后,f^Sn 就是所求的变换了。

 这样我们就能在 O(n2n) 快速求出 f 的莫比乌斯变换了。(逆莫比乌斯变换同理)

于是我们就解决了集合并卷积的问题。

UPD:

我们都知道第一层循环枚举集合,第二层循环枚举它为1的位,把去掉这个1的子集的答案加上去的做法是错的。我们考虑两个集合s,t,其中tst可能有多种路径到达s,也就是存在多个k,ks,tk,这样t就会被算多次。

这里有一个感性理解的方法,为什么第一层枚举位第二层枚举集合是对的,也就是每一个集合它的所有子集的贡献只被算了一次。

我们假设k1t并上第一个和s不一样的位,我们发现t的答案会先算到k1上,而对于其他的k,在t的答案算上来的时候自己的答案已经会先算上去了。

而对于逆莫比乌斯变换,如果理解了莫比乌斯变换后,其本质就是一个容斥。

 

void Fmt(int *a) {
  for (int i = 0; i < n; ++i)
    for (int s = 0; s < U; ++s)
      if (s >> i & 1) a[s] = Add(a[s], a[s ^ (1 << i)]);
}
void Ifmt(int *a) {
  for (int i = 0; i < n; ++i)
    for (int s = 0; s < U; ++s)
      if (s >> i & 1) a[s] = Sub(a[s], a[s ^ (1 << i)]);
}

(此处n为集合大小, U=2n

 

接下来我们来继续讲一讲子集卷积:

问题是已知 fg,我们想求出 h=fg,其中 hS=TSfTgST

 回顾刚刚的集合并卷积,子集卷积的条件比集合并卷积更苛刻,即 LR 的集合应该不相交。

考虑集合并卷积合法当且仅当 LR=,我们可以在卷积时多加一维,维护集合的大小,如 fi,S 表示集合中有 i 个元素,集合表示为 S。可以发现当 iS 的真实元素个数符合时才是对的。

初始时,我们只把 fbc[S],S 的值赋成原来的 fSg 同理),然后对每一个fi做一遍FMT,点值相乘时这么写:hi,S=j=0ifj,Sgij,S。最后扫一遍把不符合实际情况的状态赋成 0即可。(bc[]表示集合元素个数,即bitcount

 

复制代码
for i = 0 to n
  Fmt(f[i])
  Fmt(g[i])
  for s = 0 to U - 1
    for j = 0 to i
      h[i][s] += f[j][s] * g[i - j][s]
  Ifmt(h[i])
for s = 0 to U - 1
  h[bc[s]][s] is the real answer
复制代码

 

posted @   Dance_Of_Faith  阅读(4255)  评论(3编辑  收藏  举报
编辑推荐:
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示