[SDOI2016]排列计数(组合数+排列)

原题链接

题意:给你n个数其中这那个数的范围为1-n,如果第i个数a[i]的值为i则这个数是稳定的,那么当前给出n个数,如果恰好有m个数是稳定的,求满足序列的方案数

解法:第一步我们先选择出m个数是稳定的就是C(n,m),那么对于其他的n-m个数我们需要保证是错综排列的,对于这个错综排列的方案定义一个函数d[i],对于d[i]来说,d[i]的递推方程为d[i] = (d[i - 1] + d[i - 2])*(i - 1),那么我们求得答案就是C(n,m) * d[n-m]

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int mod = 1e9 + 7;
const int maxn = 2e6 + 5;
ll fact[maxn],inv[maxn];
ll d[maxn];
ll qpow(ll a, ll b , ll mod){
    ll ans = 1;
    while(b){
        if(b & 1)ans=ans*a%mod;
        a=a*a%mod;
        b>>=1;
    }
    return ans;
}
void init(){
    fact[0] = fact[1] = 1;
    for(int i = 2; i < maxn; i++){
        fact[i]=fact[i-1]*i%mod;
    }
    inv[maxn - 1] = qpow(fact[maxn - 1],mod-2,mod);
    for(int i = maxn-2; i >= 0; i--){
        inv[i] = inv[i + 1]*(i+1)%mod;
    }
    
    d[0] = 1,d[1] = 0,d[2] = 1;
    //如果当前有两个位置错综 那么方案数就为
    //1
    //3 2 3 1 3 1 2
    for(ll i = 3; i < maxn; i++){
        d[i] = (d[i - 1] + d[i - 2])*(i - 1);
        d[i] %= mod;
    }
}
ll C(ll n, ll m, ll mod){
    if(n < 0 || m < 0 || m > n)return 0;
    if(m == 0 || m == n)return 1;
    return fact[n]*inv[m]%mod*inv[n-m]%mod;
}
int main(){
    init();
    int t;
    scanf("%d",&t);
    while(t--){
        ll n,m;
        scanf("%lld%lld",&n,&m);
        ll ans = C(n,m,mod)*d[n-m]%mod;
        printf("%lld\n",ans);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值