【題解】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\)
鏈上的問題就非常清晰了,模擬一下可以得出
\[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;
}