题目链接
hdu1521
题目概述
给出 中元素和这些元素的数量,计算选择出 件物品的可能的排列的数目,其中
解题思路
指数型母函数的概念题,<组合数学>这本书中有一个定理:
很明显这个题就是上面定理的概念题,已知 和 ,计算 排列的个数.
下面的表格给出了一次计算的过程:
下面通过这个表格给出计算
的计算过程:
(表格A(上一轮计算的结果))
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
---|---|---|---|---|---|---|---|---|---|
1 |
(表格B(这一轮计算的结果))
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
---|---|---|---|---|---|---|---|---|---|
1 | |||||||||
1 | |||||||||
(整理之后是这一轮计算的值(对应程序中的temp))
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
---|---|---|---|---|---|---|---|---|---|
1 | 2 |
(更新这一轮计算的结果准备计算下一轮(对应程序中的buf))
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
---|---|---|---|---|---|---|---|---|---|
1 | 2 |
代码实现
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 15;
double buf[N];
double temp[N];
ll F[N];
ll nums[N] = {2,3,1};void calculate(int n = 3){fill(buf, buf + N, 0);for (int i = 0;i <= nums[0];i++){buf[i] = 1.0 / F[i];}// for (int i = 0; i < N; ++i){// printf("%.1f ", buf[i]);// }// printf("\n");fill(temp, temp + N, 0);ll sum = nums[0];for (int k = 1; k < n; ++k){for (int i = 0; i <= sum; ++i){for(int j = 0; j <= nums[k]; j++){temp[i + j] += buf[i]/F[j];}}// for (int i = 0; i < N; ++i){// printf("%.0f ", temp[i]*F[i]);// }// printf("\n");memcpy(buf, temp, sizeof(double) * N);fill(temp, temp + N, 0);sum += nums[k];}
}int main(int argc, const char** argv) {F[0] = 1ll;for(int i = 1; i < N; i++){F[i] = F[i - 1] * i;}calculate();int n,m;while (~scanf("%d%d",&n, &m)){for (int i = 0;i < n; ++i){scanf("%lld", &nums[i]);}calculate(n);printf("%.0f\n", buf[m]*F[m]);}return 0;
}
这个程序有以下几点要注意的:
-
那个sum表示的是已经计算出的最高次项,由当前的元素出现的次数决定;
-
第二个是因为最后的buf存储的系数实际是除以阶乘之后的,所以要用
double
来存储,并且在最后输出结果是要再乘以对应的阶乘. -
就是这里面表示第二个乘式的 的变化的
j
每一步的增长是一步,可以从上面的定理中看出.