• 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();
    }
    
    
  • 相关阅读:
    Minimum Path Sum
    Unique Paths II
    Unique Paths
    Rotate List
    Permutation Sequence
    Merge Intervals
    Jump Game
    Group Anagrams
    Combination Sum II
    评分
  • 原文地址:https://www.cnblogs.com/a1b3c7d9/p/10796857.html
Copyright © 2020-2023  润新知