不要我为啥更这题,因为这种题,代码简单,思路简单.
dp:
先讲讲错排公式,D[1]=0,D[2]=1.这个是初始值.
下面讲讲转移方程:D[i]=(n-1)*(D[i-1]+D[i-2]).
考虑把第n个元素放到任意一个地方,那就有n-1种放法,假定我放的位子为k,那么我k就有两种放法,第一种是放到n的位子,那就是D[i-2],第二种是不放到n的位子,那就相当于i-1的一个错排,D[i-1].
数学:(容斥原理)总方案数为n!,放对一个位子的方案数是C(n,1)An-1,其中必定包含放对两个的方案数,C(n,2)An-2...容斥下去即可得到通项公式.
反演暂时不熟悉,以后再更~
代码如下:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=1e9+7;
const int N=1e6+5;
ll D[N],fact[N];
ll qp(ll a,ll b)
{
ll res=1;
while(b)
{
if(b&1) res=res*a%mod;
a=a*a%mod;
b>>=1;
}
return res;
}
inline ll C(ll n,ll m)
{
ll res=fact[n];
return res*qp(fact[m],mod-2)%mod*qp(fact[n-m],mod-2)%mod;
}
void init()
{
for(int i=3;i<=N-5;i++)
{
D[i]=(i-1)*(D[i-2]+D[i-1])%mod;
}
fact[1]=1;fact[0]=1;
for(int i=2;i<=N-5;i++)
{
fact[i]=i*fact[i-1]%mod;
}
}
int main()
{
D[1]=0;D[2]=1;D[0]=1;
init();
int T;
ll n,m;
scanf("%d",&T);
while(T--)
{
scanf("%lld%lld",&n,&m);
ll ans=D[n-m]*C(n,m)%mod;
printf("%lld\n",ans);
}
return 0;
}
京公网安备 11010502036488号