[BZOJ4517] [Sdoi2016] 排列计数 (数学)
Description
求有多少种长度为 n 的序列 A,满足以下条件:
- 1 ~ n 这 n 个数在序列中各出现了一次
- 若第 i 个数 A[i] 的值为 i,则称 i 是稳定的。序列恰好有 m 个数是稳定的
满足条件的序列可能很多,序列数对 10^9+7 取模。
Input
第一行一个数 T,表示有 T 组数据。
接下来 T 行,每行两个整数 n、m。
T=500000,n≤1000000,m≤1000000
Output
输出 T 行,每行一个数,表示求出的序列数
Sample Input
5
1 0
1 1
5 2
100 50
10000 5000
1 0
1 1
5 2
100 50
10000 5000
Sample Output
0
1
20
578028887
60695423
1
20
578028887
60695423
HINT
Source
Solution
我们选$m$个数稳定,其余$n - m$个数不稳定,那么方案数即为错位全排列
选$m$个数的方案有$C_{n}^{m}$种,乘起来即可。
排列数计算除法用乘法逆元替代,好像用exgcd常数小一些= =
错排公式:$f[n] = (n - 1)(f[n - 1] + f[n - 2]) = nf[n - 1] + (-1)^{n}$
1 #include <bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 const ll MOD = 1000000007; 5 ll a[1000005], f[1000005], inv[1000005]; 6 7 ll pow(ll x) 8 { 9 ll ans = 1, y = MOD - 2; 10 for(; y; y >>= 1, x = x * x % MOD) 11 if(y & 1) ans = ans * x % MOD; 12 return ans; 13 } 14 15 ll C(int x, int y) 16 { 17 return f[x] * inv[x - y] % MOD * inv[y] % MOD; 18 } 19 20 int main() 21 { 22 int t, n, m; 23 f[0] = 1, a[0] = 1, a[1] = 0; 24 for(int i = 1; i <= 1000000; i++) 25 f[i] = f[i - 1] * i % MOD; 26 for(int i = 0; i <= 1000000; i++) 27 inv[i] = pow(f[i]); 28 for(int i = 2; i <= 1000000; i++) 29 a[i] = (a[i - 1] + a[i - 2]) * (i - 1) % MOD; 30 scanf("%d", &t); 31 while(t--) 32 { 33 scanf("%d%d", &n, &m); 34 if(m <= n) printf("%lld\n", C(n, m) * a[n - m] % MOD); 35 else puts("0"); 36 } 37 return 0; 38 }