AcWing 212. 计数交换
思路全是看算法竞赛进阶指南,我自己是不会做这题的...
很详细的题解GO
解释一下为什么答案要乘上多重集排序的公式. 假设一个序列 2 3 4 5 6 1. 我们将5和 3互换,就可以得到 2 5 6 1和 4 3两个自环,答案的Fx*Fy是将这两个自环整理为有序的序列.形成 1 2 5 6和 3 4.但此时还没有排列完.因为x环和y环的操作顺序是可以变化的.也就是说多重集排列是将x环和y环的操作进行排列.但是x环和y环的操作不会互相影响,所以能将x环的操作看成相同的元素,y环同理
1 #include <iostream> 2 #include <algorithm> 3 #include <cstdio> 4 #include <cstring> 5 using namespace std; 6 typedef long long ll; 7 const int N = 1e5+10,mod = 1e9+9; 8 ll fact[N],infact[N],plan[N]; 9 bool vis[N]; 10 int p[N],loop[N],cnt; 11 int dfs(int i)//找环长度 12 { 13 vis[i] = 1; 14 if(vis[p[i]]) return 1; 15 return 1+dfs(p[i]); 16 } 17 ll qsm(ll a,ll k,ll q) 18 { 19 ll res = 1; 20 while(k) 21 { 22 if(k&1) res = res*a%q; 23 k>>=1; 24 a = a*a%q; 25 } 26 return res; 27 } 28 void inits() 29 { 30 plan[1] = fact[0] = infact[0] = 1;//环长度为1,说明已经排好了.不用操作视为一种方案 31 for(int i=1;i<=N-10;i++) 32 { 33 fact[i] = fact[i-1]*i%mod; 34 infact[i] = infact[i-1]*qsm(i,mod-2,mod); 35 } 36 for(int i=2;i<=N-10;i++) plan[i] = qsm(i,i-2,mod); 37 } 38 int main() 39 { 40 int T,n; 41 scanf("%d",&T); 42 inits(); 43 while(T--) 44 { 45 ll ans = 1; 46 memset(vis,0,sizeof vis); cnt = 0; 47 scanf("%d",&n); 48 for(int i=1;i<=n;i++) scanf("%d",&p[i]);//i->p[i] 49 for(int i=1;i<=n;i++) if(!vis[i]) loop[++cnt] = dfs(i); 50 for(int i=1;i<=cnt;i++)//不能直接输出plan就完事,因为序列由多个环构成 51 ans = ans*infact[loop[i]-1]%mod*plan[loop[i]]%mod; 52 ans = ans*fact[n-cnt]%mod; 53 printf("%lld\n",ans); 54 } 55 return 0; 56 }