題目連結: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 }