hihoCoder#1698 : 假期计划 组合数

题面:hihoCoder#1698 : 假期计划  组合数

题解:

  题目要求是有序的排列,因此我们可以在一开始就乘上A!*B!然后在把这个序列划分成很多段。

  这样的话由于乘了阶乘,所以所有排列我们都已经统计到了,因为划分段的时候乘了组合数,所以每段里面的不同排列都已经统计到了,所以就可以解决这道题了。

  主要难度在与平时我们计算方案时一般都是先划分,再乘阶乘,所以如果陷入这个误区就可能做不出来了。

  所以我们先枚举中间那段有多长,然后乘一下阶乘和处理划分的组合数,最后再乘一下中间这段的第一个可以放在多少个位置。

  因为左右两段都必须非空,所以左边要预留一位,所以第一个可以放的位置在2,同理,最后一个可以放的位置在n - i.减一下就可以得到可以放在n - i - 1个位置。

  注意一下枚举的时候中间的天数最多n - 2,原因同上

  $ans = A! \cdot B! \cdot \binom{A - 1}{n - i - 1} \cdot \binom{B - 1}{i - 1} \cdot (n - i - 1)$   

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define R register int
 4 #define AC 101000
 5 #define p 1000000009
 6 #define LL long long
 7 
 8 LL A_ = 1, B_ = 1, A, B, t, all, ans;
 9 LL C[2][AC], inv[AC];
10 
11 void cal(LL n, int x)
12 {
13     C[x][0] = 1;
14     for(R i = 1; i <= t; i ++)
15         C[x][i] = C[x][i - 1] * (n - i + 1) % p * inv[i] % p;
16 }
17 
18 void pre()
19 {
20     scanf("%lld%lld%lld", &t, &A, &B);
21     inv[0] = inv[1] = 1;
22     for(R i = 2; i <= t; i ++) inv[i] = (p - p / i) * inv[p % i] % p;     
23     for(R i = 2; i <= A; i ++) A_ = A_ * i % p;
24     for(R i = 2; i <= B; i ++) B_ = B_ * i % p;
25     cal(A - 1, 0), cal(B - 1, 1);
26     all = A_ * B_ % p;
27 }
28 
29 void work()
30 {
31     int b = min(t - 2, B);
32     for(R i = 1; i <= b; i ++)//枚举中间的天数
33         ans = (ans + all * C[0][t - i - 1] % p * C[1][i - 1] % p * (t - i - 1) % p) % p;
34     printf("%lld\n", ans);
35 }
36 
37 int main()
38 {
39     freopen("in.in", "r", stdin);
40     pre();
41     work();
42     fclose(stdin);
43     return 0;
44 }
View Code

猜你喜欢

转载自www.cnblogs.com/ww3113306/p/9911844.html
今日推荐