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 }

 

posted @ 2021-01-30 11:51  acmloser  阅读(96)  评论(0编辑  收藏  举报