Counting swaps

Counting swaps

给你一个1~n的排列,问用最少的交换次数使之变为递增排列的方案数(mod 10^9+7),1 ≤ n ≤ 10^5。

显然最少的交换次数不定,还得需要找到最小交换次数,而考虑到交换为复杂的过程,考虑状态的性质,所以不难想到画出,+为箭头指向方向

 _   _ 
| + | +
2 1 4 3
+ | + |
|_| |_|

于是你会发现实际上我们的变换为递增序列,即把所有的环都变成自环,而交换两个数字即拆环,所以不难知道,一个环拆掉的最少的次数为环的大小-1(因为你对一个环进行一次交换操作,就变成了两个环,以此类推,拆成n个环,要操作n-1次)。

有了这样一个想法,于是考虑环的组合计数问题的方法,可以以拆环为状态划分来设递推方程,于是设(f[i])表示长度为i的环的变成全部是自环的最少操作次数的方案数,拆成两个环又有不同的拆分方式,于是设(T[i][j])表示拆成两个长i,j的环的方案数,于是我们有

[T[i][j]=i==j?i+j>>1:i+j ]

[f[i]=sum_{j=1}^{[i/2]}f[j] imes f[i-j] imes T[j][i-j] imesfrac{(i-1)!}{(j-1)!(i-j-1)!} ]

于是,设初始序列为长(l_1,l_2...,l_m)的环构成的,易知

[ans=(n-m)!prod_{i=1}^mf[l_i]frac{1}{(l_i-1)!} ]

但是对于f找规律,我们发现(f[i]=i^{i-2}),于是我们可以利用矩阵快速幂,时间复杂度应为(O(nlog(n)))

参考代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#define il inline
#define ri register
#define ll long long
#define yyb 1000000009
#define lsy 1000000007
#define _ putchar('
')
using namespace std;
int num[100001];
bool check[100001];
ll dp[100001],jc[100001],jv[100001];
il ll pow(ll,ll);
il void prepare();
template<class free>void pen(free);
template<class free>il void read(free&);
int main(){
    int cjx,i,j,n,len,tot;ll ans;
    prepare(),read(cjx);
    while(cjx--){
        read(n),memset(check,0,sizeof(check));
        for(i=1;i<=n;++i)read(num[i]);tot&=0,ans=1;
        for(i=1;i<=n;++i)
            if(!check[i]){
                j=i,++tot,len&=0;
                do j=num[j],++len,check[j]|=true;
                while(i!=j);
                ans=ans*dp[len]%yyb*jv[len-1]%yyb;
            }ans=ans*jc[n-tot]%yyb,pen(ans),_;
    }
    return 0;
}
template<class free>
void pen(free x){
    if(x>9)pen(x/10);putchar(x%10+48);
}
il ll pow(ll x,ll y){
    ll ans(1);
    while(y){
        if(y&1)ans=ans*x%yyb;
        x=x*x%yyb,y>>=1;
    }return ans;
}
void prepare(){
    ri int i,j;jc[1]=jc[0]=jv[1]=jv[0]=dp[1]=1;
    for(i=2;i<=100000;++i)
        jc[i]=jc[i-1]*i%yyb,jv[i]=
            pow(jc[i],lsy),dp[i]=pow(i,i-2);
}
template<class free>
il void read(free& x){
    x&=0;ri char c;while(c=getchar(),c<'0'||c>'9');
    while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
}

原文地址:https://www.cnblogs.com/a1b3c7d9/p/10796857.html