BZOJ-4517 [Sdoi2016]排列计数(错排公式)
题目描述
求有多少种长度为 \(n\) 的序列 \(A\),满足以下条件:
$ 1$ ~ \(n\) 这 \(n\) 个数在序列中各出现了一次。
若第 \(i\) 个数 \(A[i]\) 的值为 \(i\),则称 \(i\) 是稳定的。序列恰好有 \(m\) 个数是稳定的。
满足条件的序列可能很多,序列数对 \(10^9+7\) 取模。
数据范围:\(T=5\times 10^5,n\leq 10^6,m\leq 10^6\)。
分析
首先从 \(n\) 个数中选择 \(m\) 个数字,其余数字错排,则方案数为 \(\dbinom{n}{m}\times dp[n-m]\)。
其中 \(dp[0]=1,dp[1]=0,dp[2]=1,dp[i]=(dp[i-1]+dp[i-2])\times (i-1)\)。
代码
#include<iostream>
#include<cstdio>
using namespace std;
const int mod=1e9+7;
const int N=1e6+10;
long long quick_pow(long long a,long long b)
{
long long ans=1;
while(b)
{
if(b&1)
ans=ans*a%mod;
a=a*a%mod;
b>>=1;
}
return ans;
}
long long dp[N+10],fac[N+10],inv[N+10];
void init()
{
fac[0]=inv[0]=1;
for(int i=1;i<=N;i++)
fac[i]=fac[i-1]*i%mod;
inv[N]=quick_pow(fac[N],mod-2);
for(int i=N-1;i>=1;i--)
inv[i]=inv[i+1]*(i+1)%mod;
dp[0]=1;dp[1]=0;dp[2]=1;
for(int i=3;i<=N;i++)
dp[i]=(dp[i-1]+dp[i-2])%mod*(i-1)%mod;
}
long long C(long long n,long long m)
{
if(m>n)
return 0;
return fac[n]*inv[m]%mod*inv[n-m]%mod;
}
int main()
{
init();
int T;
cin>>T;
while(T--)
{
long long n,m;
scanf("%lld %lld",&n,&m);
printf("%lld\n",dp[n-m]*C(n,m)%mod);
}
return 0;
}
posted on 2020-11-09 17:31 DestinHistoire 阅读(93) 评论(0) 收藏 举报