HDU-1261 字串數(高精度,組合數學)
阿新 • • 發佈:2019-02-04
字串數
一個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;
}
}