[BZOJ4517] [Sdoi2016] 排列计数 (数学)

Description

  求有多少种长度为 n 的序列 A,满足以下条件:
  1. 1 ~ n 这 n 个数在序列中各出现了一次
  2. 若第 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

Sample Output

0
1
20
578028887
60695423

HINT 

Source

  鸣谢Menci上传

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 }
View Code
posted @ 2016-04-20 20:56  CtrlCV  阅读(244)  评论(0编辑  收藏  举报