1. 程式人生 > >HDU-1261 字串數(高精度,組合數學)

HDU-1261 字串數(高精度,組合數學)

字串數

一個A和兩個B一共可以組成三種字串:”ABB”,”BAB”,”BBA”.
給定若干字母和它們相應的個數,計算一共可以組成多少個不同的字串.
Input
每組測試資料分兩行,第一行為n(1<=n<=26),表示不同字母的個數,第二行為n個數A1,A2,…,An(1<=Ai<=12),表示每種字母的個數.測試資料以n=0為結束.
Output
對於每一組測試資料,輸出一個m,表示一共有多少種字串.
Sample Input
2
1 2
3
2 2 2
0
Sample Output
3
90

思路:對於這種排列問題,我們可以先考慮沒有重複情況,也就是全排列n!。然後要把重複的部分給除掉,也就是要除去n1!n2!n3!……,然後就是答案了,不過這題資料很大,要用高精度。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <cstdlib>
#include <cmath>
#include <vector>
#include <queue>
#include <map>
#include <algorithm>
#include <set>
#include <functional>
using namespace
std; typedef long long LL; typedef unsigned long long ULL; const int INF = 1e9 + 5; const int MAXN = 10005; const int MOD = 30021; const double eps = 1e-8; const double PI = acos(-1.0); const int L = 1005; int a[L]; string fac(int n) { string ans; if (n == 0) return "1"; fill(a, a + L, 0); int
s = 0, m = n; while (m) a[++s] = m % 10, m /= 10; for (int i = n - 1; i >= 2; i--) { int w = 0; for (int j = 1; j <= s; j++) a[j] = a[j] * i + w, w = a[j] / 10, a[j] = a[j] % 10; while (w) a[++s] = w % 10, w /= 10; } while (!a[s]) s--; while (s >= 1) ans += a[s--] + '0'; return ans; } int sub(int *a, int *b, int La, int Lb) { if (La<Lb) return -1;//如果a小於b,則返回-1 if (La == Lb) { for (int i = La - 1; i >= 0; i--) if (a[i]>b[i]) break; else if (a[i]<b[i]) return -1;//如果a小於b,則返回-1 } for (int i = 0; i<La; i++)//高精度減法 { a[i] -= b[i]; if (a[i]<0) a[i] += 10, a[i + 1]--; } for (int i = La - 1; i >= 0; i--) if (a[i]) return i + 1;//返回差的位數 return 0;//返回差的位數 } string div(string n1, string n2, int nn)//n1,n2是字串表示的被除數,除數,nn是選擇返回商還是餘數 { string s, v;//s存商,v存餘數 int a[L], b[L], r[L], La = n1.size(), Lb = n2.size(), i, tp = La;//a,b是整形陣列表示被除數,除數,tp儲存被除數的長度 fill(a, a + L, 0); fill(b, b + L, 0); fill(r, r + L, 0);//陣列元素都置為0 for (i = La - 1; i >= 0; i--) a[La - 1 - i] = n1[i] - '0'; for (i = Lb - 1; i >= 0; i--) b[Lb - 1 - i] = n2[i] - '0'; if (La<Lb || (La == Lb && n1<n2)) { //cout<<0<<endl; return n1; }//如果a<b,則商為0,餘數為被除數 int t = La - Lb;//除被數和除數的位數之差 for (int i = La - 1; i >= 0; i--)//將除數擴大10^t倍 if (i >= t) b[i] = b[i - t]; else b[i] = 0; Lb = La; for (int j = 0; j <= t; j++) { int temp; while ((temp = sub(a, b + j, La, Lb - j)) >= 0)//如果被除數比除數大繼續減 { La = temp; r[t - j]++; } } for (i = 0; i<L - 10; i++) r[i + 1] += r[i] / 10, r[i] %= 10;//統一處理進位 while (!r[i]) i--;//將整形陣列表示的商轉化成字串表示的 while (i >= 0) s += r[i--] + '0'; //cout<<s<<endl; i = tp; while (!a[i]) i--;//將整形陣列表示的餘數轉化成字串表示的</span> while (i >= 0) v += a[i--] + '0'; if (v.empty()) v = "0"; //cout<<v<<endl; if (nn == 1) return s; if (nn == 2) return v; } string jc[13] = { "0","1","2","6","24","120","720","5040","40320","362880","3628800","39916800","479001600" }; int main() { int n; int a[30]; int sum; string an; while (scanf("%d",&n)!=EOF) { if (n == 0) break; sum = 0; for (int i = 1; i <= n; i++) { scanf("%d", &a[i]); sum += a[i]; } an = fac(sum); for (int i = 1; i <= n; i++) { an = div(an, jc[a[i]],1); } cout << an << endl; } }