题目大意:有 n 种物品,每种物品有一个数量,让你从中选一些物品构造 m 排列,输出排列数量。
参考:https://wenku.baidu.com/view/846bb51bce84b9d528ea81c758f5f61fb7362892.html
直接构造生成函数: ( 1 + x + x 2 2 ! + . . . + x 1 a a 1 ! ) ∗ ( 1 + x + x 2 2 ! + . . . + x 2 a a 2 ! ) ∗ . . ∗ ( 1 + x + x 2 2 ! + . . . + x n a a n ! ) (1 + x + \frac{x^2}{2!} +...+ \frac{x^a_1}{a_1!})*(1 + x + \frac{x^2}{2!} +...+ \frac{x^a_2}{a_2!})*..*(1 + x + \frac{x^2}{2!} +...+ \frac{x^a_n}{a_n!}) (1+x+2!x2+...+a1!x1a)∗(1+x+2!x2+...+a2!x2a)∗..∗(1+x+2!x2+...+an!xna) x m x^m xm的系数 乘上 m!就是答案
小结:普通多项式生成函数可以处理组合方案问题:形如有1分,5分,7分的硬币无限种,凑出2角的方案数。
而指数型生成函数可以处理排列方案数问题。
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e3 + 10;
int n,m;
double g1[maxn],g2[maxn];
double pw[maxn];
int v[maxn];
int main() {
while(~scanf("%d%d",&n,&m)) {
for(int i = 1; i <= n; i++) {
scanf("%d",&v[i]);
}
pw[0] = 1;
for(int i = 1; i <= 15; i++)
pw[i] = pw[i - 1] / i;
memset(g1,0,sizeof g1);
memset(g2,0,sizeof g2);
for(int i = 0; i <= v[1]; i++) g1[i] = pw[i];
for(int i = 2; i <= n; i++) {
for(int j = 0; j <= m; j++) {
for(int k = 0; k + j <= m && k <= v[i]; k++) {
g2[k + j] += g1[j] * pw[k];
}
}
for(int j = 0; j <= m; j++) {
g1[j] = g2[j];
g2[j] = 0;
}
}
printf("%.0lf\n",g1[m] / pw[m]);
}
return 0;
}