[SDOI2016]排列计数

[SDOI2016]排列计数

有一个长度为n的1~n的全排列,如果一个数i出现在第i个位置上,则称该数是稳定的,询问恰好有m个数是稳定的全排列的方案数,(n≤1000000,m≤1000000)

显然为组合计数题,于是我们来相办法划分问题,如果事先选出m个数让其稳定,不难得知剩下的问题就是一个错排问题,而对于错排问题,设(f_i)表示长度为i的错排方案数,我们又有递推公式(f_i=(i-1) imes(f_{i-1}+f_{i-2})),边界(f_2=1),把两者相乘即答案。

参考代码:

#include <iostream>
#include <cstdio>
#define il inline
#define ri register
#define yyb 1000000007
#define ll long long
using namespace std;
ll f[1000001],jc[1000001],jv[1000001];
il ll pow(ll,ll),c(int,int);
il void read(int&),prepare(int);
int main(){
    int lsy;read(lsy),prepare(1000000);
    while(lsy--){
        int n,m;
        read(n),read(m);
        printf("%lld
",c(n,m)*f[n-m]%yyb);
    }
    return 0;
}
il ll c(int n,int r){
    if(n<r)return 0;
    return jc[n]*jv[r]%yyb*jv[n-r]%yyb;
}
il ll pow(ll x,ll y){
    ll ans(1);while(y){
        if(y&1)(ans*=x)%=yyb;
        (x*=x)%=yyb,y>>=1;
    }return ans;
}
il void prepare(int n){
    f[0]=jv[0]=f[2]=jc[0]=1;
    for(int i(3);i<=n;++i)
        f[i]=(i-1)*(f[i-1]+f[i-2])%yyb;
    for(int i(1);i<=n;++i)jc[i]=jc[i-1]*i%yyb;
    jv[n]=pow(jc[n],yyb-2);
    for(int i(n);i>1;--i)jv[i-1]=jv[i]*i%yyb;
}
il void read(int &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/10932315.html