《算法竞赛进阶指南》0x36组合计数 计数交换

题目链接:https://www.acwing.com/problem/content/214/

给出一个排列打乱之后的顺序,要求将其还原成升序全排列的方法数,通过计算,将一个长度为n的环变成自环需要用n-1步,将一个长度为n的环变成自环可能的步数有n^(n-2)种,假设这个给出的全排列中环的数量是k,那么一共需要n-k步。由于每次步数的选择中,每一类的是没有顺序的,所以需要利用多重集的公式进行去重。

代码:

#include<iostream>
#include<cstdio>
#include<string.h>
using namespace std;
const int maxn = 100010;
const int mod = 1000000009;
typedef long long ll;
ll jc[maxn];
int n;
int a[maxn],p[maxn];
bool vis[maxn];
ll power(ll a,ll b){
    ll ans=1;
    b%=mod;
    while(b){
        if(b&1)
            ans=(ans*a)%mod;
        a=(a*a)%mod;
        b>>=1;
    }
    return ans;
}
int main(){
    jc[0]=1;
    for(int i=1;i<=maxn-1;i++)jc[i]=(jc[i-1]*i)%mod;
    int T;
    cin>>T;
    while(T--){
        cin>>n;
        int cnt=0;
        ll ans=1;
        memset(vis,0,sizeof(vis));
        for(int i=1;i<=n;i++)scanf("%d",&a[i]);
        for(int i=1;i<=n;i++)p[i]=a[i];
        for(int i=1;i<=n;i++){
            if(vis[i])continue;
            int len=1;    
            vis[i]=1;
            for(int j=p[i];j!=i;j=p[j])vis[j]=1,len++;
            cnt++;
            ans=(ans*(len==1?1:power(len,len-2)))%mod;
            ans=(ans*power(jc[len-1],mod-2))%mod;//求解逆元 
        }
        ans=(ans*jc[n-cnt])%mod;
        cout<<ans<<endl;
    }    
    return 0;
}

 

posted @ 2020-07-10 16:14  WA自动机~  阅读(175)  评论(0编辑  收藏  举报