【科技】快速莫比烏斯變換(反演) 與 子集卷積

咱們比較瞭解的是有關多項式的乘法運算,對於下標爲整數,下標運算爲相加等於某個數的時候,咱們有很優秀的FFT作法。算法

可是遇到一些奇怪的卷積形式時,好比咱們定義 $h = f * g$, $h_{S} = \sum\limits_{L \subseteq S}^{} \sum\limits_{R \subseteq S}^{} [L \cup R = S] f_{L} * g_{R}$。spa

此時下標是一個集合,運算爲集合並的卷積,咱們已知了 $f$ 和 $g$ ,須要快速算出 $h$。code

最暴力的作法是 $O(2^{n})$ 分別枚舉 $L$ 和 $R$,把答案加到 $h$ 中去,這樣複雜度是 $O(4^{n})$,不太行。blog

這時咱們就要一種高效的算法。求卷積能夠用分治乘法,好像比較高妙,但咱們要講的是另外一種:快速莫比烏斯變換和反演。it

類比FFT,FMT也須要先把 $f$ 和 $g$ 求點值,點值相乘後再插值回去,快速莫比烏斯變換就至關於點值,快速莫比烏斯反演就至關於插值。class

具體證實:原理

  • 咱們定義 $f$ 的莫比烏斯變換爲 $\hat{f}$ ,其中 $\hat{f_{S}} = \sum\limits_{T \subseteq S}^{} f_{T}$。
  • 相反的,咱們定義 $\hat{f}$ 的莫比烏斯反演爲 $f$,其中 $f_{S} = \sum\limits_{T \subseteq S}^{} (-1)^{|S| - |T|} \hat{f_{T}}$,用容斥原理易得。
  • 而後咱們對卷積式兩邊同時作莫比烏斯變換:$\hat{h_{S}} = \sum\limits_{L \subseteq S} \sum\limits_{R \subseteq S} [L \cup R \subseteq S] f_{L} * g_{R}$。
  • 因爲 $[L \cup R \subseteq S] \Leftrightarrow [L \subseteq S][R \subseteq S]$,因此 $\hat{h_{S}} = \sum\limits_{L \subseteq S} \sum\limits_{R \subseteq S} f_{L} * g_{R}$。
  • 即 $\hat{h_{S}} = ( \sum\limits_{L \subseteq S} f_{L} ) * ( \sum\limits_{R \subseteq S} g_{R} ) = \hat{f_{S}} * \hat{g_{S}}$。

因而問題就在於如何快速求出 $f$ 和 $g$ 莫比烏斯變換(反演)。循環

若是要暴力的話,能夠直接枚舉子集算出莫比烏斯變換(反演),這樣是 $O(3^{n})$,雖然比較優秀了,但複雜度還能更低。方法

咱們考慮用遞推解決:im

  • 設 $\hat{f_{S}}^{(i)}$ 表示 $\sum\limits_{T \subseteq S} [(S - T) \subseteq \{1, 2, ..., i\}] f_{T}$
  • 易得初始狀態:$\hat{f_{S}}^{(0)} = f_{S}$
  • 對於每個不包含 $\{i\}$ 的集合 $S$,可知 $\hat{f_{S}}^{(i)} = \hat{f_{S}}^{(i - 1)}$(由於 $S$ 並無 $i$ 這位),$\hat{f}_{S \cup \{i\}}^{(i)} = \hat{f}_{S}^{(i - 1)} + \hat{f}_{S \cup \{i\}}^{(i - 1)}$(前者的 $T$ 沒有包含 $\{i\}$,然後者的 $T$ 必須包含了 $\{i\}$)。
  • 顯然,遞推了 $n$ 輪以後,$\hat{f}_{S}^n$ 就是所求的變換了。

 這樣咱們就能在 $O(n * 2^{n})$ 快速求出 $f$ 的莫比烏斯變換了。(逆莫比烏斯變換同理)

因而咱們就解決了集合並卷積的問題。

UPD:

咱們都知道第一層循環枚舉集合,第二層循環枚舉它爲$1$的位,把去掉這個$1$的子集的答案加上去的作法是錯的。咱們考慮兩個集合$s, t$,其中$t \in s$。$t$可能有多種路徑到達$s$,也就是存在多個$k, k \in s, t \in k$,這樣$t$就會被算屢次。

這裏有一個感性理解的方法,爲何第一層枚舉位第二層枚舉集合是對的,也就是每個集合它的全部子集的貢獻只被算了一次。

咱們假設$k_{1}$爲$t$並上第一個和$s$不同的位,咱們發現$t$的答案會先算到$k_{1}$上,而對於其餘的$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 = 2^n$)

 

接下來咱們來繼續講一講子集卷積:

問題是已知 $f$ 和 $g$,咱們想求出 $h = f * g$,其中 $h_{S} = \sum\limits_{T \subseteq S} f_{T} * g_{S - T}$。

 回顧剛剛的集合並卷積,子集卷積的條件比集合並卷積更苛刻,即 $L$ 和 $R$ 的集合應該不相交。

考慮集合並卷積合法當且僅當 $L \cap R = \varnothing$,咱們能夠在卷積時多加一維,維護集合的大小,如 $f_{i,S}$ 表示集合中有 $i$ 個元素,集合表示爲 $S$。能夠發現當 $i$ 和 $S$ 的真實元素個數符合時纔是對的。

初始時,咱們只把 $f_{bc[S],S}$ 的值賦成原來的 $f_{S}$($g$ 同理),而後對每個$f_i$作一遍FMT,點值相乘時這麼寫:$h_{i, S} = \sum\limits_{j = 0}^{i} f_{j,S} * g_{i - j, 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
相關文章
相關標籤/搜索