[SDOI2016]排列计数

[SDOI2016]排列计数

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

显然为组合计数题,于是我们来相办法划分问题,如果事先选出m个数让其稳定,不难得知剩下的问题就是一个错排问题,而对于错排问题,设\(f_i\)表示长度为i的错排方案数,我们又有递推公式\(f_i=(i-1)\times(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\n",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();
}
上一篇:[SDOI2016]数字配对


下一篇:[SDOI2016]储能表