洛谷——P4071 [SDOI2016]排列计数(错排+组合数学)
P4071 [SDOI2016]排列计数
求有多少种长度为 n 的序列 A,满足以下条件:
1 ~ n 这 n 个数在序列中各出现了一次
若第 i 个数 A[i] 的值为 i,则称 i 是稳定的。序列恰好有 m 个数是稳定的
满足条件的序列可能很多,序列数对 $10^9+7$取模。
显然此题的答案就是$C(n,m)*d[n-m]$
求解组合数$C(n,m)$使用通项公式$\frac{n!}{m!\times (n-m)!}$
由于$n,m$很大,所以要预处理出$n!$
由于$10^9+7$是个质数,所以根据费马小定理求逆元
错排公式 $d_n=(n-1)*(d_{n-1}+d_{n-2})$
$d_1=0,d_2=1$
推导方法:
错排吗,每一个数都不在其原来的位置上
1.假设把$n$放在$k$($k<n$)上,那么就有$n-1$种方法
2.考虑放$k$,若把$k$放在位置$n$上,有$d_{n-2}$,反之有$d_{n-1}$种方法
#include<iostream> #include<cstdio> #include<cmath> #include<algorithm> #define LL long long #define N 1000005 #define mod (LL)1000000007 using namespace std; LL d[N],T,inv[N],f[N]; LL pow(LL a,LL b){ LL s=1; for(;b;b>>=1,a=a*a%mod) if(b&1) s=s*a%mod; return s; } LL C(LL n,LL m){ LL del=(inv[m]%mod*inv[n-m]%mod)%mod; LL x; if(del<=N-4) x=inv[n]*f[del]%mod; else x=inv[n]*pow(del,mod-2)%mod; return (x%mod+mod)%mod; } int main() { scanf("%lld",&T); d[1]=0,d[2]=1; for(int i=3;i<=N-4;i++) d[i]=(((i-1)%mod)*(d[i-1]+d[i-2])%mod)%mod; inv[0]=1; for(int i=1;i<=N-4;i++) inv[i]=inv[i-1]*i%mod; f[1]=1; for(int i=2;i<=N-4;i++) f[i]=(mod-mod/i)*f[mod%i]%mod; while(T--){ LL n,m; scanf("%lld%lld",&n,&m); if(n==m) printf("1\n"); else printf("%lld\n",C(n,m)*d[n-m]%mod); } return 0; }
~~蒟蒻(博主)又刷了一道水题。。。~~
博主蒟蒻,若有出错的地方,敬请指出。
如有侵犯您版权的地方,请快速联系我,我会撤回本博文。