题目描述:有n栋楼排成一排,高度分别为1, 2……n,从前向后看可以看到F栋楼,从后往前看可以看到B栋楼,问这n栋楼有多少种排列?
分析:先说一下第一类斯特林数吧。
S(n, m)表示恰包含m个圈n个元素的排列数,则很容易证明有递推公式:S(n, m) = (n - 1) * S(n - 1, m) + S(n - 1, m - 1);
方便记忆一下吧:假设当前有n - 1个元素,如果又来一个元素形成了m个圈,则当前n - 1个元素只能是m个圈或是m - 1个圈
(1) 如果当前是m个圈,且又来一个元素仍然是m个圈,我们考虑把第n个元素插在第i个元素的后面且和第i个元素在同一个圈,则一共有(n - 1)* S(n - 1, m)
(2) 如果当前是m - 1个圈,又来一个元素形成了m个圈,则第n个元素只能自己形成一个圈,则只有S(n - 1, m - 1)
综上:S(n, m) = (n - 1) * S(n - 1, m) + S(n - 1, m - 1)
第一类斯特林数还满足母函数关系:
设fn(x) = x * (x - 1) * (x - 2) * …… *(x - n + 1), S(n, m)是fn(x)的展开式中x^m的系数。
回到这个题上来:
无论是从前看还是从后看,n一定可以被看到且是最后一个被看到的,我们考虑把剩下的n - 1个元素分成 f + b - 2个组,每组内部有排列,从这f + b - 2组中选出f - 1个组放在前面,容易得出ans = C(f + b - 2, f - 1) * S(n - 1, f + b - 2);
1 #include <stdio.h> 2 #include <string.h> 3 #define mod 1000000007 4 #define maxn 2005 5 long long C[maxn][maxn], S[maxn][maxn]; 6 7 void get_S() 8 { 9 memset(C, 0, sizeof(C)); 10 memset(S, 0, sizeof(S)); 11 12 C[0][0] = 1; 13 S[0][0] = 1; 14 for(int i = 1; i < maxn; i++) 15 { 16 C[i][0] = 1; 17 S[i][0] = 0; 18 for(int j = 1; j <= i; j++) 19 { 20 C[i][j] = (C[i - 1][j] + C[i - 1][j - 1]) % mod; 21 S[i][j] = (((i - 1) * S[i - 1][j]) % mod + S[i - 1][j - 1]) % mod; 22 } 23 } 24 return ; 25 } 26 27 int main() 28 { 29 get_S(); 30 int T, n, f, b; 31 scanf("%d", &T); 32 while(T--) 33 { 34 scanf("%d%d%d", &n, &f, &b); 35 long long ans = C[f + b - 2][f - 1] * S[n - 1][f + b - 2] % mod; 36 printf("%I64d\n", ans); 37 } 38 return 0; 39 }