Description
多组询问,求有多少(1)到(n)的排列(a),满足序列恰好有(m)个位置(i),使得(a_i = i)。
答案对(10^9 + 7)取模。
Solution
算是错排的板子题了。
错排,就是对于某个(1sim{n})的排列,满足(forall{iin[1,n]},a_i e{i})。
那么这道题其实就是求(dbinom{n}{m}D(n-m))。
现在要解决如何求(D(n)),有两种方法求:
首先是递推:对于一个错排(D(n)),不失一般性,考虑(1)这个数放在哪个位置。它肯定不能放在(a_1),剩下的位置随便放,所以先乘上一个(n-1)。然后再考虑(2)放在哪里:如果(2)放在(a_1),那么就是一个子问题(D(n-2));如果(2)不能放在(a_1),那(a_1)其实就相当于(a_2),这时是一个子问题(D(n-1))。综上,(D(n)=(n-1)(D(n-1)+D(n-2))),特别地,(D(0)=1,D(1)=0)。
其次是容斥: 正整数(1, 2, 3, …, n)的全排列有 (n!) 种,其中第(k)位是(k)的排列有((n-1)!)种;当(k)分别取(1, 2, 3, …, n)时,共有(dbinom{n}{1}*(n-1)!)种排列是至少放对了一个的,由于所求的是错排的种数,所以应当减去这些排列;但是此时把同时有两个数不错排的排列多排除了一次,应补上;在补上时,把同时有三个数不错排的排列多补上了一次,应排除......继续这一过程,得到错排的排列种数为
(D(n)=sumlimits_{i=0}^n(-1)^idbinom{n}{i}(n-i)!)
Code
#include <bits/stdc++.h>
using namespace std;
#define ll long long
const ll mod = 1e9 + 7;
ll t, n, m, pw[1000005], inv[1000005], wp[1000005];
ll read()
{
ll x = 0; char ch = getchar();
while (ch < '0' || ch > '9') ch = getchar();
while (ch >= '0' && ch <= '9') {x = x * 10 + ch - '0'; ch = getchar();}
return x;
}
ll qpow(ll base, ll p)
{
ll res = 1;
while (p)
{
if (p & 1) res = res * base % mod;
base = base * base % mod;
p >>= 1;
}
return res;
}
int main()
{
t = read();
wp[0] = 1; wp[1] = 0; wp[2] = 1;
for (ll i = 3; i <= 1000000; i ++ )
wp[i] = (i - 1) * ((wp[i - 1] % mod) + (wp[i - 2] % mod)) % mod;
pw[1] = 1;
for (ll i = 2; i <= 1000001; i ++ )
pw[i] = pw[i - 1] * i % mod;
inv[1000001] = qpow(pw[1000001], mod - 2);
for (ll i = 1000000; i >= 0; i -- )
inv[i] = inv[i + 1] * (i + 1) % mod;
while (t -- )
{
n = read(); m = read();
printf("%lld
", (((pw[n] * inv[m]) % mod) * inv[n - m] % mod) * wp[n - m] % mod);
}
return 0;
}