1. 程式人生 > 其它 >【題解】ABC214G - Three Permutations

【題解】ABC214G - Three Permutations

很神的數數題,技巧性和思維難度都很高。

我們要求不存在一個位置相同,直接容斥轉化為對於每個 \(i\in[0,n]\) 求存在 \(i\) 個位置相同。

題目的限制條件是給定恰好兩個排列 \(P,Q\),而不是給定多個排列,考慮其中的意義。

對於排列,等價於一個由若干個環組成的有向圖。思考一下不難發現如果我們將 \(i\to P_i\) 的邊改為 \(P_i\to Q_i\) 的邊,這仍然是一個由若干個環組成的有向圖。

為什麼要這樣轉換,因為轉換之後,一個位置變成了一條邊,那麼一個位置相等,等價於選擇一條邊的一個節點。

所以我們可以先選出若干條邊,然後對每條邊固定一個節點,兩條邊固定的節點不能相同。

根據圖的性質,選出的邊構成的子圖,由簡單鏈和簡單環和自環組成。

對於自環,只能固定自己。對於簡單環,只有固定每條邊的起點和固定每條邊的中點兩種方案。

對於一條鏈,顯然存在一個斷點,對於斷點前面的點固定起點,後面的點固定終點,所以由鏈的節點數種方案。

原圖中不同環之間互不干擾,所以對每個環分開求。

由於環的相似性,我們可以直接定義 \(g[i][j]\) 表示 \(i\) 個點的環,選了 \(j\) 條邊,有多少種選邊並固定點的方案。

由於環是對稱的,直接求非常麻煩,我們列舉環上第一節點所在鏈的長度 \(k\) ,然後轉化為求 \(f[i][j]\) 表示長度為 \(i\) 的鏈,選了 \(j\)

條邊的方案。顯然有

\[g[i][j] = \sum\limits_{k = 0}^j(k+1)^2f[i-k-1][j-k] \]

鏈上的問題就非常清晰了,模擬一下可以得出

\[f[i][j] = \sum\limits_{k = i - j - 1}^{i- 1}(i-k)f[k][j - (i - k - 1)] \]

最後我們將所有環的答案合併,本質就是一個揹包。

對於 \(f\) ,字首和優化做到 \(\mathcal{O}(N^2)\),對於 \(g\),我們只用求特定的 \(i\),且 \(\sum i = n\),所以總的時間複雜度也是 \(\mathcal{O}(N^2)\)

官方題解的組合數做法好神啊,沒看懂(

#define N 3005

int f[N][N], g[N], n, p[N], q[N], u[N], c[N], v[N], fac[N], d[N], s[N][N], t[N][N];
int calc(int x){
	v[x] = 1;
	if(!v[u[x]])return calc(u[x]) + 1;
	return 1;
}
int main() {
	//int T = read();while(T--)solve();
	n = read(), fac[0] = 1;
	rp(i, n)fac[i] = 1LL * fac[i - 1] * i % P;
	rp(i, n)p[i] = read();
	rp(i, n)q[i] = read(), u[p[i]] = q[i];
	
	//calc :: f
	f[0][0] = s[0][0] = 1;
	rp(i, n){
		rep(j, 0, i - 1){
			ad(f[i][j], 1LL * i * s[i - 1][i - j - 1] % P);
			su(f[i][j], t[i - 1][i - j - 1]);
			s[i][i - j] = (s[i - 1][i - j] + f[i][j]) % P;
			t[i][i - j] = (t[i - 1][i - j] + 1LL * i * f[i][j] % P) % P;
		}
		s[i][0] = 1;
	}
	c[0] = 1; int sz = 0;
	rp(i, n)if(!v[i]){
		memset(d, 0, sizeof(d));
		memset(g, 0, sizeof(g));
		int ss = calc(i);
		
		//calc :: g
		g[ss] = 2, g[0] = 1;
		rep(j, 1, ss - 1)
			rep(k, 0, j)
				ad(g[j], 1LL * (k + 1) * (k + 1) * f[ss - k - 1][j - k] % P);
		if(1 == ss)g[ss] = 1;
		
		//calc :: c
		rep(j, 0, sz)rep(k, 0, ss)
			ad(d[j + k], 1LL * c[j] * g[k] % P);
		sz += ss;
		rep(j, 0, sz)c[j] = d[j];
		
	}
	int ans = 0, op = 1;
	
	rep(i, 0, n){
		ad(ans, 1LL * fac[n - i] * (P + op * c[i]) % P), op = -op;
	}
	cout << ans << endl;
	return 0;
}