Light oj 1095 - Arrange the Numbers (组合数学+递推)

题目链接:http://www.lightoj.com/volume_showproblem.php?problem=1095

题意:

        给你包含1~n的排列,初始位置1,2,3...,n,问你刚好固定前m个数中的k个数的位置,问你有多少中排列方案。(比如5 3 2有1 4 3 2 5这种方案,1和3固定了)

思路:

        前m个取k个就是C(m, k)个方案。然后就是类似错排的思想,设dp[i]为i个数在初始位置各不相同。其中的组合数用逆元算出。

        ans = dp[m - k] * C(n - m, 0) + dp[m - k + 1] * C(n - m, 1) .. dp[n - k] * C(n - m, n - m),这个式子表示取后面n-m个数的某些数 与 前面的m - k个数形成错排,剩下的数位置不变。

        最后就是ans * C(m, k)

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 typedef long long LL;
 4 LL mod = 1e9 + 7, dp[1005];
 5 LL f[1005];
 6 LL fpow(LL a, LL n) {
 7     LL ans = 1;
 8     while(n) {
 9         if(n & 1)
10             ans = ans * a % mod;
11         a = a * a % mod;
12         n >>= 1;
13     }
14     return ans;
15 }
16 int main()
17 {
18     dp[0] = 1;
19     dp[1] = 0, dp[2] = 1;
20     f[1] = f[0] = 1, f[2] = 2;
21     for(LL i = 3; i <= 1000; ++i) {
22         dp[i] = (dp[i - 1] + dp[i - 2]) % mod * (LL)(i - 1) % mod;
23         f[i] = f[i - 1] * (LL)i % mod;
24     }
25     int n, m, k, t;
26     scanf("%d", &t);
27     for(int ca = 1; ca <= t; ++ca) {
28         scanf("%d %d %d", &n, &m, &k);
29         if(n == m && m - k == 1) {
30             printf("Case %d: 0\n", ca);
31             continue;
32         }
33         LL ans = f[m] * fpow(f[k]*f[m - k]%mod, mod - 2) % mod;
34         int temp = m - k, temp2 = n - m;
35         LL res = 0;
36         for(int i = temp; i <= temp2 + temp; ++i) {
37             res = (res + dp[i] * f[temp2] % mod * fpow(f[i - temp]*f[temp2 - i + temp] % mod, mod - 2) % mod) % mod;
38         }
39         printf("Case %d: %lld\n", ca, ans * res % mod);
40     }
41     return 0;
42 }

 

posted @ 2016-10-14 21:52  Recoder  阅读(516)  评论(0编辑  收藏  举报