这个题描述的本身就很离散,所以就相当于选出m个位置,剩下的位置的错排
错排的公式也不是很难推,注意及时利用以前的结果就好了,实在不行根据印象打表找规律
设有n个数 ,n个位置,第一个数一定不能选第一个位置,所以可选的位置就有 n-1个
考虑剩下的n-1个数,一定有一个数可以放在1的位置,那这个数放在1的位置的方案数就等于这个数不放在1的位置+这个数放在1的位置= f(n-1)+f(n-2)
虽然感觉应该是个递归到1的过程,但实际上只用到n-2
错排的公式也不是很难推,注意及时利用以前的结果就好了,实在不行根据印象打表找规律
设有n个数 ,n个位置,第一个数一定不能选第一个位置,所以可选的位置就有 n-1个
考虑剩下的n-1个数,一定有一个数可以放在1的位置,那这个数放在1的位置的方案数就等于这个数不放在1的位置+这个数放在1的位置= f(n-1)+f(n-2)
虽然感觉应该是个递归到1的过程,但实际上只用到n-2
码:
#include<iostream>
#include<cstdio>
using namespace std;
#define leaves 1000000007
#define ll long long
#define N 1000005
ll jc[N],f[N],ni[N],T,n,m,x,y,i;
void exgcd(ll a,ll b)
{
if(!b)
{
x=1;
y=0;
return ;
}
exgcd(b,a%b);
ll t=x;
x=y;
y=t-a/b*y;
}
ll C(ll a,ll b)
{
return jc[b]*ni[a]%leaves*ni[b-a]%leaves;
}
int main()
{
f[0]=1;
f[1]=0;
f[2]=1;
ll sx=1000000;
for(i=3;i<=sx;i++)
{
f[i]=(i-1)*(f[i-1]+f[i-2])%leaves;
}
jc[0]=1;
for(i=1;i<=sx;i++)
jc[i]=jc[i-1]*i%leaves;
exgcd(jc[sx],leaves);
ni[sx]=x;
for(i=sx-1;i>=0;i--)
ni[i]=ni[i+1]*(i+1)%leaves;
scanf("%lld",&T);
while(T--)
{
scanf("%lld%lld",&n,&m);
printf("%lld\n",(C(m,n)*f[n-m]%leaves+leaves)%leaves);
}
}