【BZOJ4517】排列计数(SDOI2016)-组合数学:错排
测试地址:排列计数
做法:本题需要用到组合数学中的错排问题。
首先,如果两个序列中稳定的位置不同,那么两个序列肯定不同,因此我们枚举稳定的位置,有种方案,然后对于剩下的元素,它们不能处在编号和它相同的位置上,学过组合数学的同学应该知道这就是经典的错排问题,令长为的排列中,对所有都有的排列数为个,我们有递推式:
边界条件为。那么本题的答案就是。
如果只是摆递推式就太没意思了,就来讲讲这个递推式是怎么来的吧。
我们枚举所在的位置,显然它不可能在号位置,那么其它任意位置它都可以选,共种选择。然后对于它选择的位置,我们讨论在不在位置上:
如果在位置上,那么其它个元素都不能在自己的位置上,方案数为。
如果不在位置上,那么它不能在位置,而其它个元素都不能在自己的位置上,方案数为。
这就是上述递推式的由来。于是我们就解决了这一题。
以下是本人代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=1000000007;
int T,n[500010],m[500010],maxn=0;
ll fac[1000010],inv[1000010],fi[1000010],D[1000010];
ll C(int n,int m)
{
return fac[n]*fi[m]%mod*fi[n-m]%mod;
}
int main()
{
scanf("%d",&T);
for(int i=1;i<=T;i++)
{
scanf("%d%d",&n[i],&m[i]);
maxn=max(maxn,n[i]);
}
fac[0]=fac[1]=inv[0]=inv[1]=fi[0]=fi[1]=1;
D[0]=1,D[1]=0;
for(ll i=2;i<=(ll)maxn;i++)
{
fac[i]=fac[i-1]*i%mod;
inv[i]=(mod-mod/i)*inv[mod%i]%mod;
fi[i]=fi[i-1]*inv[i]%mod;
D[i]=(i-1)*(D[i-1]+D[i-2])%mod;
}
for(int i=1;i<=T;i++)
printf("%lld\n",C(n[i],m[i])*D[n[i]-m[i]]%mod);
return 0;
}