[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();
}

转载于:https://www.cnblogs.com/a1b3c7d9/p/10932315.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值