[總結] 快速莫比烏斯變換和子集卷積
阿新 • • 發佈:2018-12-17
馬上noip了我在學點啥
問題
求集合並卷積,即\(h_S=\sum_{L\in S}\sum_{R\in S}[L\cup R]f_L*g_R\)
要求更嚴一點,求子集卷積,即\(h_S=\sum_{L\in S}\sum_{R\in S}[L\cup R=S][L\cap R=\varnothing]f_L*g_R=\sum_{L\in S}f_L*g_{S-L}\)
Sol
先看集合並卷積
最暴力的做法就是\(O(2^n)\)分別列舉\(L,R\),\(O(4^n)\)的將答案加到\(h\)裡去 我覺得不行
下面是個高妙做法:
- 定義\(f\)的莫比烏斯變換為\(\hat f\),其中\(\hat {f_{S}}=\sum_{T\in S}f_T\)
- 我們對卷積式兩邊同時做莫比烏斯變換:\(\hat{h_S}=\sum_{L\in S}\sum_{R\in S}[L\cup R\in S]f_L*g_R\)。
- 由於\([L\cup R\in S]=[L\in S][R\in S]\),所以\(\hat{h_S}=\sum_{L\in S}\sum_{R\in S}f_L*g_R\)。
- 即\(\hat{h_S}=(\sum_{L\in S}f_L)*(\sum_{R\in S}g_R)\)。
於是問題就在如何快速求出\(f\)和\(g\)莫比烏斯變換。
這東西是個子集和,可以高維字首和優化到\(O(n\times 2^n)\)。
但是我們求出來的只是\(\hat{h_S}\)
下面我們看子集卷積
它的條件比集合並卷積更苛刻,要求\(L\)和\(R\)的集合不能相交。
我們可以在卷積時多加一維,維護集合的大小,如\(f_{i,S}\)表示集合中有\(i\)個元素,集合表示為\(S\)。顯然,當\(i\)和\(S\)的真實元素個數相等時才是合法的。初始時,我們只把\(f_{cnt|S|,S}\)的值賦成原來的\(f_S\),然後做一遍莫比烏斯變換,\(h_{i,S}=\sum_{j=0}^if_{j,S}*g_{i-j,S}\)。
還是上程式碼吧
void FMT(int *A, int o) {// o 為識別因子 for (int i = 1; i < ST; i <<= 1)//ST-1 表示全集 for (int j = 0; j < ST; j++) if (i&j) (A[j] += A[j^i]*o) %= mod; } for (int i = 0; i <= n; i++) FMT(g[i], 1); for (int i = 0; i <= n; i++) FMT(f[i], 1); for (int i = 1; i <= n; i++) { for (int j = 0; j <= i; j++) for (int k = 0; k < ST; k++) (h[i][k] += 1ll*f[j][k]*g[i-j][k]%mod) %= mod; FMT(h[i], -1); for (int k = 0; k < ST; k++) if (cnt[k] != i) h[i][k] = 0; if (i != n) FMT(h[i], 1); }