AcWing212 計數交換

這個式子就不再推導了,書上寫的很明確,但感覺解釋的不是很明白,別的博客都是直接抄的原文,於是寫一下自己的理解。

f[i]=\sum_{x+y=i} f[x]*f[y]*T(x,y)*\frac{(i-2)!}{(x-1)!(y-1)!}

解釋下這個式子

 

1、爲什麼不同x,y之間可以直接相加。

第一步的交換是把大環 i 拆成 x,y 兩個小環,不同的 x,y 第一步當然不同。那麼無論後面怎麼排,第一步不同,相互之間肯定無重複。

 

2、爲什麼可以直接 *T(x,y) 。

同樣的x,y,環裏的數不同,那麼在拆 i 的時候第一步一定不同,同上。

 

3、爲什麼有多重集。

對於拆成的兩個環,自己需要多少步形成自環x,y之間是沒有關係的,所以是乘法原理。

這個時候有個問題,直接乘是先x再y的數量,對於x,y兩個環交叉進行的數量並沒有統計在內,對於每一種x的方案,y的方案,交叉進行的時候,我們不改變所有x環交換的相對順序,比如 假設x有3步,X1,X2,X3,我們在x環和y環組合一起的序列中,不改變X1,X2,X3的相對位置,X1還在X2前面,但有可能X1和X2之間插入了Y1。

這樣的話,就構成了一個多重集。

 

 

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <string>
#include <vector>
#include <queue>
#include <map>
#include <set>
#define ms(a,b) memset(a,b,sizeof(a))
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
typedef double ab;
const int N=1e5+5;
const ll mod=1e9+9;
ll jc[N],jc_inv[N],f[N];
int n;
int a[N],vis[N];
vector<int> ans;
void exgcd(ll a,ll b,ll &x,ll &y)
{
    if(!b)
    {
        x=1;
        y=0;
        return ;
    }
    exgcd(b,a%b,y,x);
    y-=(a/b)*x;
}
ll qpow(ll a, ll b)
{
    ll res=1;
    while(b)
    {
        if(b&1)
            res=res*a%mod;
        b>>=1;
        a=a*a%mod;
    }
    return res;
}
void init()
{
    f[1]=jc[0]=jc_inv[0]=jc[1]=jc_inv[1]=1;
    for(int i=2;i<=N-5;i++)
    {
        f[i]=qpow(i,i-2);
        jc[i]=jc[i-1]*i%mod;
        ll x,y;
        exgcd(jc[i],mod,x,y);
        jc_inv[i]=(x%mod+mod)%mod;
    }
}
int main()
{
    int T;
    init();
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++) scanf("%d",&a[i]);
        ll ans=1;
        int l=0;
        for(int i=1,len=1;i<=n;i++,len=1)
        {
            if(vis[i]) continue;
            vis[i]=1;
            for(int j=a[i];!vis[j];j=a[j])
            {
                vis[j]=1;
                ++len;
            }
            ans=ans*f[len]%mod;
            ans=ans*jc_inv[len-1]%mod;
            ++l;
        }
        ans=ans*jc[n-l]%mod;
        printf("%lld\n",ans);
        for(int i=1;i<=n;i++)
            vis[i]=0; 
    }
}

 

發佈了74 篇原創文章 · 獲贊 4 · 訪問量 5056
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章