题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4372
题意:
有n栋高楼横着排成一排,各自的高度为1到n的一个排列。
从左边看可以看到f栋楼,从右边看可以看到b栋楼,并且高的楼会挡住低的楼。
问你这些楼有多少种排列方法。
题解:
由于高的楼会挡住低的楼,所以这些楼首先会被划分成f+b-2个区域(除去中间最高的楼),并且左边有f-1个,右边有b-1个。
对于一个区域(假设在左边),这个区域由若干栋楼组成,并且最高的楼一定在最左边。
那么,由一个区域中的元素组成的任意一个环排列,在这个区域中都有唯一的放法,因为要把最高的元素拉到最左边。
所以,原题被简化为:将n-1个元素形成f+b-2个环排列,并将其中f-1个环放在左边的方法数。
又是第一类Stirling数。
· 将n-1个元素形成f+b-2个环排列的方法数 = S(n-1,f+b-2)
· 将其中f-1个环放在左边的方法数 = C(f+b-2,f-1)
所以答案为:S(n-1,f+b-2)*C(f+b-2,f-1)
注:此题有不合法数据,要判断一下是否f+b-1>n,如果是,输出0(不合法)。
AC Code:
1 // n: tot f: lef b: rig 2 // lef group = f-1 3 // rig group = b-1 4 // elem num = n-1 5 // circle num = f+b-2 6 // ans = s(n-1, f+b-2) * c(f+b-2, f-1) 7 // s(n,k) = s(n-1,k-1) + (n-1)*s(n-1,k) 8 9 #include <iostream> 10 #include <stdio.h> 11 #include <string.h> 12 #define MAX_N 2005 13 #define MOD 1000000007 14 15 using namespace std; 16 17 int n,f,b,t; 18 long long s[MAX_N][MAX_N]; 19 long long c[MAX_N][MAX_N]; 20 21 void cal_stirling() 22 { 23 memset(s,0,sizeof(s)); 24 s[0][0]=1; 25 for(int i=1;i<MAX_N;i++) 26 { 27 s[i][i]=1; 28 for(int j=1;j<i;j++) 29 { 30 s[i][j]=(s[i-1][j-1]+(i-1)*s[i-1][j])%MOD; 31 } 32 } 33 } 34 35 void cal_combination() 36 { 37 memset(c,0,sizeof(c)); 38 c[0][0]=1; 39 for(int i=1;i<MAX_N;i++) 40 { 41 c[i][0]=1; 42 for(int j=1;j<=i;j++) 43 { 44 c[i][j]=(c[i-1][j]+c[i-1][j-1])%MOD; 45 } 46 } 47 } 48 49 int main() 50 { 51 cal_stirling(); 52 cal_combination(); 53 cin>>t; 54 for(int cas=1;cas<=t;cas++) 55 { 56 cin>>n>>f>>b; 57 if(f+b-1<=n) cout<<(s[n-1][f+b-2]*c[f+b-2][f-1])%MOD<<endl; 58 else cout<<0<<endl; 59 } 60 }
时间: 2024-10-05 23:08:56