不要我为啥更这题,因为这种题,代码简单,思路简单.
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;
}