FMT 和 子集卷積


FMT 和 子集卷積

FMT

給定數列 $ a_{0\dots 2^{k}-1} $ 求 $ b $ 滿足 $ b_{s} = \sum_{i\in s} a_i $

實現方法很簡單,

for( i in 0 to n-1 ) 
	for( j in 0 to 2^n-1)
		if( j & ( 1 << i ) ) 
			a[j] += a[j ^ ( 1 << i )]

然后稱為 $ B = \text{FMT}(A) $ ,快速莫比烏斯變換

想要還原也很簡單,把代碼反着寫:

for( i in n-1 downTo 0 ) 
	for( j in 2^n - 1 downTo 0)
		if( j & ( 1 << i ) ) 
			a[j] -= a[j ^ ( 1 << i )]

當然, $ i $ 的順序可以是原來的順序,因為按照哪個順序枚舉位根本不重要

同時,$ j $ 的順序也不重要,考慮對於一個數字,它只有在當前枚舉的位數為 1 的時候才被執行,所以就算已經枚舉到這位是 0 的狀態,它也不會被更新。

所以甚至只需要改個符號就是逆變換了

for( i in 0 to n-1 ) 
	for( j in 0 to 2^n-1)
		if( j & ( 1 << i ) ) 
			a[j] -= a[j ^ ( 1 << i )]

這樣 $ A = \text{IFMT}( B ) $

FMT 可以寫成 FFT 那樣的形式,就不贅述了。

或卷積

或卷積就需要用到這個東西。

或卷積是指:

\[C_s = \sum_{i|j=s} A_i B_j \]

有一個結論, $ \text{FMT}(C) = \text{FMT}(A) \cdot \text{FMT}(B) $ ,其中 \(\cdot\) 指點積,也就是把每個位置的函數值乘起來。

原因是 \((i \cup j) \sube s\) 等價於 \((i \sube s)\and(j \sube s)\) 。於是

\[\begin{aligned} \ [x]FMT(C) &=\sum_{s \sube x}C_s\\ &= \sum_{s \sube x}\sum_{i|j = s}A_iB_j\\ &= \sum_{i|j \sube x} A_iB_j\\ &= ( \sum_{i\sube x} A_i )(\sum_{j\sube x} B_j)\\ &= [x]FMT(A) \cdot [x]FMT(B) \end{aligned} \]

所以有 $ \text{FMT}(C) = \text{FMT}(A) \cdot \text{FMT}(B) ) $

於是可以 $ O(n2^n) $ 做這個。

子集卷積

子集卷積長這樣:

\[C_s = A\times_{subset} B = \sum_{i|j=s,i\&j = 0} A_i B_j \]

如果設 $ p(x) $ 為 $ x $ 的 popcount( 1 的個數),那么:

\[(i|j = s) , (i\&j = 0) \Leftrightarrow i|j = s , p(i)+p(j) = p(s)\\C_s = \sum_{i|j = s , p(i)+p(j) = p(s)} A_iB_j \]

我們把 $ C $ 擴展到二維,設 $ C'_{x,k} $,定義如下:

\[C'_{x,s} = \sum_{i|j = s,p(i)+p(j) = x} A_iB_j[p(s) = x] \]

把 $ C $ 擴展到二維了,$ A $ 也擴展到二維,定義 $ A'_{p,s} $

\[A'_{x,s} = \left\{\begin{aligned}&0 & {p(s) \neq x}\\&A_{s} & {p(s) = x} \end{aligned}\right. \]

同理定義 $ b'_{x,s} $

我們知道

\[C'_{x,s} = \sum_{i|j = s,p(i)+p(j) = x} A'_{p(i),i} B'_{p(j),j} \]

觀察到 \(C'_x = \sum_{i=0}^x A'_{i}\times_{or} B'_{x-i}\) ,而且需要最后去掉左邊的 \(p(s) \neq x\) 的情況。

這樣復雜度 $ O(n^3 2^n) $,一共要卷 $ n^2 $ 次。

注意 $ \text{FMT} , \text{IFMT} $ 都有可加性,所以我們把那個或卷積寫成 $ \text{FMT} $ 的形式

\[\begin{aligned}C'_{x} &= \sum_i IFMT(FMT(A'_i) · FMT(B'_{x-i}))\\&= IFMT( \sum_i FMT(A_i') · FMT(B_{x-i}') )\end{aligned} \]

我們現在只需要處理出所有 $ A'i $ 和 $ B'{i} $ 的 FMT ,最后再跑 $ n $ 次逆 FMT ,所以這樣做就優化到了 $ O( 2^n n^2 ) $

不知道為啥 開O2 TLE 了。。。注意模數是 $ 10^9+9 $ 不是 $ 10^9+7 $。。。目害成功wa了兩發

#include "iostream"
#include "algorithm"
#include "cstring"
#include "cstdio"
using namespace std;
#define P 1000000009
int rd( ) {
    char ch = ' '; int ret = 0;
    while( ch > '9' || ch < '0' ) ch = getchar();
    while( ch >= '0' && ch <= '9' ) ret = ret * 10 + ch - '0' , ch = getchar();
    return ret;
}

int A[1<<21] , B[1<<21] , n , len;
int a[21][1<<21] , b[21][1<<21] , c[21][1<<21];

void FMT( int A[] , int l ) {
    for( int i = 0 ; i < l ; ++ i )
        for( int j = 0 ; j < ( 1 << l ) ; ++ j )
            if( j & ( 1 << i ) ) A[j] = ( A[j] + A[j ^ ( 1 << i )] ) % P;
}
void IFMT( int A[] , int l ) {
    for( int i = 0 ; i < l ; ++ i )
        for( int j = 0 ; j < ( 1 << l ) ; ++ j )
            if( j & ( 1 << i ) ) A[j] = ( A[j] + P - A[j ^ ( 1 << i )] ) % P;
}

int main() {
    cin >> n; len = ( 1 << n );
    for( int i = 0 ; i < len ; ++ i ) A[i] = rd() , a[__builtin_popcount(i)][i] = A[i];
    for( int i = 0 ; i < len ; ++ i ) B[i] = rd() , b[__builtin_popcount(i)][i] = B[i];
    for( int i = 0 ; i <= n ; ++ i ) FMT( a[i] , n ) ,  FMT( b[i] , n );
    for( int x = 0 ; x <= n ; ++ x ) {
        for( int i = 0 ; i <= x ; ++ i )
            for( int j = 0 ; j < ( 1 << n ) ; ++ j )
                ( c[x][j] += 1ll * a[i][j] * b[x - i][j] % P ) %= P;
        IFMT( c[x] , n );
    }
    for( int i = 0 ; i < len ; ++ i ) printf("%d ",c[__builtin_popcount(i)][i]);
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM