HDU-1261 字串数(高精度,组合数学)

左手的ㄟ右手 2022-06-09 11:15 288阅读 0赞

字串数

一个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!……,然后就是答案了,不过这题数据很大,要用高精度。

  1. #include <iostream>
  2. #include <cstdio>
  3. #include <cstring>
  4. #include <string>
  5. #include <cstdlib>
  6. #include <cmath>
  7. #include <vector>
  8. #include <queue>
  9. #include <map>
  10. #include <algorithm>
  11. #include <set>
  12. #include <functional>
  13. using namespace std;
  14. typedef long long LL;
  15. typedef unsigned long long ULL;
  16. const int INF = 1e9 + 5;
  17. const int MAXN = 10005;
  18. const int MOD = 30021;
  19. const double eps = 1e-8;
  20. const double PI = acos(-1.0);
  21. const int L = 1005;
  22. int a[L];
  23. string fac(int n)
  24. {
  25. string ans;
  26. if (n == 0) return "1";
  27. fill(a, a + L, 0);
  28. int s = 0, m = n;
  29. while (m) a[++s] = m % 10, m /= 10;
  30. for (int i = n - 1; i >= 2; i--)
  31. {
  32. int w = 0;
  33. for (int j = 1; j <= s; j++) a[j] = a[j] * i + w, w = a[j] / 10, a[j] = a[j] % 10;
  34. while (w) a[++s] = w % 10, w /= 10;
  35. }
  36. while (!a[s]) s--;
  37. while (s >= 1) ans += a[s--] + '0';
  38. return ans;
  39. }
  40. int sub(int *a, int *b, int La, int Lb)
  41. {
  42. if (La<Lb) return -1;//如果a小于b,则返回-1
  43. if (La == Lb)
  44. {
  45. for (int i = La - 1; i >= 0; i--)
  46. if (a[i]>b[i]) break;
  47. else if (a[i]<b[i]) return -1;//如果a小于b,则返回-1
  48. }
  49. for (int i = 0; i<La; i++)//高精度减法
  50. {
  51. a[i] -= b[i];
  52. if (a[i]<0) a[i] += 10, a[i + 1]--;
  53. }
  54. for (int i = La - 1; i >= 0; i--)
  55. if (a[i]) return i + 1;//返回差的位数
  56. return 0;//返回差的位数
  57. }
  58. string div(string n1, string n2, int nn)//n1,n2是字符串表示的被除数,除数,nn是选择返回商还是余数
  59. {
  60. string s, v;//s存商,v存余数
  61. int a[L], b[L], r[L], La = n1.size(), Lb = n2.size(), i, tp = La;//a,b是整形数组表示被除数,除数,tp保存被除数的长度
  62. fill(a, a + L, 0); fill(b, b + L, 0); fill(r, r + L, 0);//数组元素都置为0
  63. for (i = La - 1; i >= 0; i--) a[La - 1 - i] = n1[i] - '0';
  64. for (i = Lb - 1; i >= 0; i--) b[Lb - 1 - i] = n2[i] - '0';
  65. if (La<Lb || (La == Lb && n1<n2)) {
  66. //cout<<0<<endl;
  67. return n1;
  68. }//如果a<b,则商为0,余数为被除数
  69. int t = La - Lb;//除被数和除数的位数之差
  70. for (int i = La - 1; i >= 0; i--)//将除数扩大10^t倍
  71. if (i >= t) b[i] = b[i - t];
  72. else b[i] = 0;
  73. Lb = La;
  74. for (int j = 0; j <= t; j++)
  75. {
  76. int temp;
  77. while ((temp = sub(a, b + j, La, Lb - j)) >= 0)//如果被除数比除数大继续减
  78. {
  79. La = temp;
  80. r[t - j]++;
  81. }
  82. }
  83. for (i = 0; i<L - 10; i++) r[i + 1] += r[i] / 10, r[i] %= 10;//统一处理进位
  84. while (!r[i]) i--;//将整形数组表示的商转化成字符串表示的
  85. while (i >= 0) s += r[i--] + '0';
  86. //cout<<s<<endl;
  87. i = tp;
  88. while (!a[i]) i--;//将整形数组表示的余数转化成字符串表示的</span>
  89. while (i >= 0) v += a[i--] + '0';
  90. if (v.empty()) v = "0";
  91. //cout<<v<<endl;
  92. if (nn == 1) return s;
  93. if (nn == 2) return v;
  94. }
  95. string jc[13] = { "0","1","2","6","24","120","720","5040","40320","362880","3628800","39916800","479001600" };
  96. int main()
  97. {
  98. int n;
  99. int a[30];
  100. int sum;
  101. string an;
  102. while (scanf("%d",&n)!=EOF)
  103. {
  104. if (n == 0)
  105. break;
  106. sum = 0;
  107. for (int i = 1; i <= n; i++)
  108. {
  109. scanf("%d", &a[i]);
  110. sum += a[i];
  111. }
  112. an = fac(sum);
  113. for (int i = 1; i <= n; i++)
  114. {
  115. an = div(an, jc[a[i]],1);
  116. }
  117. cout << an << endl;
  118. }
  119. }

发表评论

表情:
评论列表 (有 0 条评论,288人围观)

还没有评论,来说两句吧...

相关阅读

    相关 Hdu-1261

    Problem Description 一个A和两个B一共可以组成三种字符串:“ABB”,“BAB”,“BBA”. 给定若干字母和它们相应的个数,计算一共可以组成多少个