「學習筆記」集合冪級數


「學習筆記」集合冪級數

本文是一篇學習筆記,具體的概念請參考2015年VFK的國家隊論文《集合冪級數的性質及其快速算法》

集合並卷積 - 快速莫比烏斯變換

我們要求形如這樣的一個卷積:

\[h_S =\sum_{L \subseteq S}\sum_{R\subseteq S} [L\cup R=S] f_L g_R \]

回憶一下之前所學的莫比烏斯反演,本質上是把質因子看成多重集合,這里的集合並等價於莫比烏斯反演的兩個數的 \(\text{lcm}\),不妨直接對這個集合做莫比烏斯變換,定義:

\[f'_S = \sum_{T\subseteq S} f_T \]

同理,也可以參考這個容斥得到:

\[f_S = \sum_{T\subseteq S} (-1)^{|S|-|T|} f'_T \]

對這個卷積左右兩邊都反演一下可以得到:

\[h'_S = \sum_{L}\sum_{R}[L \cup R \subseteq S]f_Lg_R \\ h'_S = \sum_{L}\sum_{R}[L \subseteq S][R \subseteq S]f_Lg_R \\ h'_S = \sum_{L}[L \subseteq S]f_L\sum_{R}[R \subseteq S]g_R \\ h'_S = f'_Sg'_S \]

然后只需要 \(O(n2^n)\) 簡單遞推出 \(f'\) 就可以求解了:

集合交卷積

我們要求形如這樣的一個卷積:

\[h_S =\sum_{L \subseteq S}\sum_{R\subseteq S} [L\cap R=S] f_L g_R \]

類似關於倍數的莫比烏斯反演,將反演的式子重新定義一下就好了:

\[f_S = \sum_{S\subseteq T} (-1)^{|T|-|S|} f'_T \]

集合對稱差卷積 - 快速沃爾什變換

我們要求形如這樣的一個卷積,其中 \(\bigoplus\) 表示異或:

\[h_S =\sum_{L \subseteq S}\sum_{R\subseteq S} [L\oplus R=S] f_L g_R \]

這里先引入一個在 VFK 《炫酷反演魔術》課件中的一個看上去沒用的東西輔助推導:

\[\frac {1} {2^n} \sum_{T \subseteq 2^n} (-1)^{|S \cap T|} = [S = \emptyset] \]

正確性可以自己驗證一下:

然后這里定義 \(f\) 的沃爾什變換 \(f'\):

\[f'_S = \sum_{T \subseteq 2^n} f_T (-1)^{|S \cap T|} \\ \]

考慮怎么求逆變換:

\[f_S = \sum_{T\subseteq 2^n} f_T [S \oplus T=\emptyset] \\ f_S = \sum_{T \subseteq 2^n} f_T \frac {1} {2^n} \sum_{A \subseteq 2^n} (-1)^{|S \oplus T\cap A|} \\ f_S = \frac {1} {2^n} \sum_{T \subseteq 2^n} f_T \sum_{A \subseteq 2^n} (-1)^{|S \cap A|}(-1)^{|T\cap A|} \\ f_S = \frac {1} {2^n} \sum_{A \subseteq 2^n}(-1)^{|S\cap A|}\sum_{T \subseteq 2^n} f_T (-1)^{|T \cap A|} \\ f_S = \frac {1} {2^n} \sum_{A \subseteq 2^n}(-1)^{|S\cap A|}f'_A \\ \]

然后對於原來的卷積形式進行一些變換:

\[h_S = \sum_{L \subseteq2^n}\sum_{R \subseteq2^n} [L\oplus R\oplus S=\emptyset]f_Lg_R\\ h_S = \sum_{L \subseteq2^n}\sum_{R \subseteq2^n} \frac {1} {2^n} \sum_{T \subseteq 2^n} (-1)^{|L\oplus R\oplus S \cap T|}f_Lg_R \\ h_S = \frac {1} {2^n}\sum_{L \subseteq2^n}\sum_{R \subseteq2^n} \sum_{T \subseteq 2^n} (-1)^{|L\cap T|}(-1)^{|R\cap T|}(-1)^{|S\cap T|}f_Lg_R \\ h_S = \frac {1} {2^n}\sum_{T \subseteq 2^n} (-1)^{|S\cap T|} \sum_{L \subseteq2^n}(-1)^{|L\cap T|}f_L\sum_{R \subseteq2^n} (-1)^{|R\cap T|}g_R \]

根據之前推出的沃爾什變換:

\[h_S = \frac {1} {2^n}\sum_{T \subseteq 2^n} (-1)^{|S\cap T|}f'_Sg'_S \\ h'_S =f'_S g'_S \]

與上面類似的,接下來只需要 \(O(n2^n)\) 遞推出 \(f'\) 即可。

子集卷積

我們要求形如這樣的一個卷積:

\[h_S =\sum_{T \subseteq S} f_T g_{(S-T)} \]

直接變換不太好做,不如加上一維 \(cnt\) 表示集合中的元素個數,並轉變一下卷積形式:

\[h_S =\sum_{L \subseteq S}\sum_{R\subseteq S} [L\cup R=S][cnt_L+cnt_R=cnt_S] f_L g_R \]

對於 \(cnt\) 相同的 \(f ,g\) 放在一起做快速莫比烏斯變換,對於 \(cnt\) 相同的 \(h\) 枚舉一個 \(L\) 的大小后直接點乘即可,復雜度 \(O(n^22^n)\)

子集卷積在遞推上的應用

可以當做上一塊的一個例題來看 「WC2018」州區划分

預處理出每一個集合划成一個州是否可行,計算出 \(g_S\) 當州可行時為 \(sum_S^p\) 否則為 \(0\)

\(f_S\) 表示當前選取集合為 \(S\) 時的滿意度之和,不難得到遞推式:

\[f_S = \sum_{T \subseteq S} \frac{f_T g_{(S-T)}}{sum_S^p} \]

提出一個 \(sum_S^p\) 可以得到:

\[\frac{f_S}{{sum_S^p}} = \sum_{T \subseteq S} f_T g_{(S-T)} \]

后面是一個熟悉的子集卷積的形式,觀察到都是由 \(cnt\)\(f\) 轉移到 \(cnt\) 大的 \(f\) ,對 \(g\) 求莫比烏斯變換后直接分層點乘即可。

注意點乘完處理回去的時候由一些細節,子集卷積后只有 \(cnt_S=\) 當前枚舉的大小的位置答案是正確的,要把不正確的位置清 \(0\) 不然會影響后面的遞推值。

Code

/*program by mangoyang*/
#include<bits/stdc++.h>
#define inf (0x7f7f7f7f)
#define Max(a, b) ((a) > (b) ? (a) : (b))
#define Min(a, b) ((a) < (b) ? (a) : (b))
typedef long long ll;
using namespace std;
template <class T>
inline void read(T &x){
	int f = 0, ch = 0; x = 0;
	for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = 1;
	for(; isdigit(ch); ch = getchar()) x = x * 10 + ch - 48;
	if(f) x = -x;
}


#define int ll
const int N = 25, Len = 5000005, mod = 998244353;

vector<int> g[N];
int f[N][Len], h[N][Len], sum[Len], dig[Len], fa[N], w[N], n, m, p;

inline void up(int &x, int y){ 
	x = (x + y >= mod ? x + y - mod : (x + y < 0 ? x + y + mod : x + y));
}
inline int ask(int x){ return x == fa[x] ? x : fa[x] = ask(fa[x]); }
inline int Pow(int a, int b){
	int ans = 1; 
	for(; b; b >>= 1, a = a * a % mod)
		if(b & 1) ans = ans * a % mod;
	return ans;
}	
inline int check(int s){
	int tot = 0;
	for(int i = 0; i < n; i++) fa[i] = i;
	for(int i = 0; i < n; i++) if((1 << i) & s){
		for(int j = 0; j < g[i].size(); j++)
			if((1 << g[i][j]) & s){
				int p = ask(i), q = ask(g[i][j]);
				if(p != q) fa[p] = q;
			}
	}
	for(int i = 0; i < n; i++) 
		tot += ((s >> i) & 1) & (fa[i] == i);
	return tot > 1;
}
inline void FMT(int A[], int sgn){
	for(int i = 0; i < n; i++)
		for(int s = 0; s < (1 << n); s++) 
			if((1 << i) & s) up(A[s], A[s^(1<<i)] * sgn);
}
signed main(){
	read(n), read(m), read(p);
	for(int i = 1, x, y; i <= m; i++){ 
		read(x), read(y), x--, y--;
		g[x].push_back(y), g[y].push_back(x);
	}
	for(int i = 0; i < n; i++) read(w[i]);
	for(int s = 1; s < (1 << n); s++){
		int flag = 0;
		for(int i = 0; i < n; i++) if((1 << i) & s){
			int deg = 0;
			for(int j = 0; j < g[i].size(); j++)
				deg += (s >> g[i][j]) & 1;
			if(deg & 1) flag = 1;
			sum[s] += w[i], dig[s]++;
		}
		if((flag || check(s)) && dig[s] != 1) 
			h[dig[s]][s] = Pow(sum[s], p);
	}
	for(int i = 0; i <= n; i++) FMT(h[i], 1);
	f[0][0] = 1, FMT(f[0], 1);
	for(int i = 1; i <= n; i++){
		for(int j = 1; j <= i; j++)
			for(int s = 0; s < (1 << n); s++)
				up(f[i][s], h[j][s] * f[i-j][s] % mod);
		FMT(f[i], -1);		
		for(int s = 0; s < (1 << n); s++) if(dig[s] == i)
			(f[i][s] *= Pow(Pow(sum[s], p), mod - 2)) %= mod;
		else f[i][s] = 0;
		if(i != n) FMT(f[i], 1);
	}
	cout << (f[n][(1<<n)-1] % mod + mod) % mod;
	return 0;
}


免責聲明!

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



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