其他分享
首页 > 其他分享> > Counting swaps

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]\times f[i-j]\times T[j][i-j]\times\frac{(i-1)!}{(j-1)!(i-j)!}\]

于是,设初始序列为长\(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('\n')
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();
}

标签:swaps,void,100001,il,ans,Counting,ll,define
来源: https://www.cnblogs.com/a1b3c7d9/p/10796857.html