2020牛客暑期多校訓練營(第六場)A - African Sort - 組合數學
阿新 • • 發佈:2020-10-07
Description
給定一個長度為 \(n\) 的排列,每次可以選擇一個子集將它 random_shuffle
,並花費子集大小的代價。求將整個排列升序排序的代價期望。
Solution
對於排列 \(P\),可以描述為一張圖,其中 \(i \to P_i\),則這張圖一定由若干個不交的環構成。我們的任務就是要使得每個環的大小都為 \(1\)。
結論:每次操作都取一個完整的環不會使答案更劣。
顯然環的內容和解決這個環的代價期望沒有任何關係,不妨設 \(f(n)\) 表示解決一個大小為 \(n\) 的環的期望代價,那麼
\[f(n)=\frac {\sum_{i=2}^n C_n^i (i-1)! (n-i)! f(i)} {n!} + n \]展開化簡一下,得到
\[f(n)=\sum_{i=2}^n \frac {f(i)} {i} + n \]將 \(f(n)\) 與 \(f(n-1)\) 的式子做差,變形得到
\[f(n) = \frac n {n-1} (f(n-1)+1) \]初態 \(f(1)=0, f(2)=4\),暴力遞推即可。
在預處理好所有 \(f(i)\) 後,我們只需要暴力找出每一個環,統計答案即可。
#include <bits/stdc++.h> using namespace std; #define int long long const int N = 1000005; const int mod = 998244353; int n,m,p[N],vis[N],f[N]; int qpow(int p,int q) { return (q&1?p:1)*(q?qpow(p*p%mod,q/2):1)%mod; } int inv(int p) { return qpow(p,mod-2); } void solve() { for(int i=1;i<=n;i++) cin>>p[i]; for(int i=1;i<=n;i++) vis[i]=0; int ans=0; for(int i=1;i<=n;i++) { if(vis[i]) continue; int x=i,cnt=0; do { ++cnt; vis[x]=1; x=p[x]; } while(vis[x]==0); ans+=f[cnt]; } cout<<ans%mod<<endl; } signed main() { ios::sync_with_stdio(false); cin>>n>>m; f[1]=0; f[2]=4; for(int i=3;i<=n;i++) { f[i]=i*inv(i-1)%mod*(f[i-1]+1)%mod; } for(int i=1;i<=m;i++) { solve(); } return 0; }