洛谷 P4609: [FJOI2016] 建筑师

本省省选题是需要做的。

题目传送门:洛谷P4609

题意简述:

求有多少个 \(1\) 到 \(N\) 的排列,满足比之前的所有数都大的数正好有 \(A\) 个,比之后的所有数都大的数正好有 \(B\) 个。

答案对 \(mod=10^9+7\) 取模。

有 \(T\) 组数据。

题解:

考虑最大的元素 \(N\) ,它把序列分成两部分。

考虑左边的一部分,它满足比之前所有数都大的数正好有 \(A-1\) 个,右边同理。

把每个比之前所有数都大的数和其右边比它小的连续一段的数分为一组,则左边有 \(A-1\) 组。

每一组中,最大的元素要放在最左边,其余的排列方式随意,所以是一个圆排列,即有代表元素的排列。

这提示我们考虑第一类斯特林数。

把左边和右边合起来考虑,总共 \(N-1\) 个元素,分成 \(A+B-2\) 个圆排列,也就是 \(\mathbf{S}_{N-1}^{A+B-2}\) 。

但是这些圆排列中,需要选取 \(A-1\) 个放在左边一部分,右边的放剩下的,也就是 \(\mathbf{C}_{A+B-2}^{A-1}\) 。

注意到确定了一部分放置哪些圆排列后,这一部分的圆排列顺序就确定了。

所以答案就是 \(\mathbf{S}_{N-1}^{A+B-2}\mathbf{C}_{A+B-2}^{A-1}\) 。

因为数据范围不大,暴力预处理第一类斯特林数和组合数即可。

 1 // 第一类斯特林数 (Stirling Number of the First Type)
 2 // 将 n 个有标号元素 分为 m 个 圆排列 (无序) 的方案数 记作 S(n,m)
 3 // 圆排列 : 轮换相同的视作相同的排列。
 4 // 圆排列的实际意义 : 有代表元素的排列 , 例如代表元素为最大元素
 5 // S(n,m) = S(n-1,m-1) + (n-1)*S(n-1,m)
 6 // 考虑第 n 个元素 , 单独成一个排列 , 或者在前 n-1 个元素中的任意一个的左侧插入
 7 // 边界 : S(0,0)=1 , S(n,0)=0
 8 
 9 #include <cstdio>
10 
11 typedef long long LL;
12 const int mod = 1000000007;
13 
14 int S[50005][205], C[205][105];
15 
16 void Init() {
17     S[0][0] = 1;
18     for (int i = 1; i <= 50000; ++i) {
19         for (int j = 1; j <= i && j <= 200; ++j) {
20             S[i][j] = (S[i - 1][j - 1] + (LL)(i - 1) * S[i - 1][j]) % mod;
21         }
22     }
23     
24     C[0][0] = 1;
25     for (int i = 1; i <= 200; ++i) {
26         C[i][0] = 1;
27         for (int j = 1; j <= i && j <= 100; ++j)
28             C[i][j] = (C[i - 1][j - 1] + C[i - 1][j]) % mod;
29     }
30 }
31 
32 int main() {
33     Init();
34     int T;
35     scanf("%d", &T);
36     while (T--) {
37         int n, A, B;
38         scanf("%d%d%d", &n, &A, &B);
39         printf("%lld\n", (LL)S[n - 1][A + B - 2] * C[A + B - 2][A - 1] % mod);
40     }
41     return 0;
42 }

 

posted @ 2018-11-24 12:37 粉兔 阅读(...) 评论(...) 编辑 收藏