HDU1521 排列组合(指数生成函数)
指数型生成函数(Exponential Generating Function, EGF)
一、EGF 的定义
对于一个序列
[a0,a1,a2,a3,…][a_0, a_1, a_2, a_3, \dots][a0,a1,a2,a3,…]
其 指数型生成函数(EGF) 定义为:
G(x)=∑n=0∞anxnn!G(x) = \sum_{n=0}^{\infty} a_n \frac{x^n}{n!} G(x)=n=0∑∞ann!xn
对比普通生成函数(OGF):
OGF: F(x)=∑anxn⟺EGF: G(x)=∑anxnn!\text{OGF: } F(x) = \sum a_n x^n \quad\Longleftrightarrow\quad \text{EGF: } G(x) = \sum a_n \frac{x^n}{n!} OGF: F(x)=∑anxn⟺EGF: G(x)=∑ann!xn
区别:EGF 中每项系数多了一个 1n!\frac{1}{n!}n!1。
二、EGF 的核心意义
- OGF:用于无序组合(只考虑选取,不考虑顺序)。
- EGF:用于有序排列(考虑顺序的情况)。
EGF 能自然地处理排列问题中出现的 1n!\frac{1}{n!}n!1 因子,
非常适合用于涉及标号、排列、顺序的计数问题。
题目要求:
有 nnn 种物品,第 iii 种有 aia_iai 个。
要从中选出 mmm 个物品进行排列(顺序不同算不同),求不同排列的总数。
数学建模
设选取第 iii 种物品 xix_ixi 个,则:
x1+x2+⋯+xn=m,0≤xi≤aix_1 + x_2 + \cdots + x_n = m, \quad 0 \le x_i \le a_i x1+x2+⋯+xn=m,0≤xi≤ai
每种选择对应的排列数为:
m!x1!,x2!⋯xn!\frac{m!}{x_1! , x_2! \cdots x_n!} x1!,x2!⋯xn!m!
生成函数表示
每种物品的 EGF 为:
fi(x)=1+x1!+x22!+⋯+xaiai!f_i(x) = 1 + \frac{x}{1!} + \frac{x^2}{2!} + \cdots + \frac{x^{a_i}}{a_i!} fi(x)=1+1!x+2!x2+⋯+ai!xai
所有物品的总生成函数为:
F(x)=∏i=1nfi(x)F(x) = \prod_{i=1}^{n} f_i(x) F(x)=i=1∏nfi(x)
取出 xmx^mxm 的系数:
[xm]F(x)=∑x1+⋯+xn=m1x1!x2!⋯xn![x^m] F(x) = \sum_{x_1+\cdots+x_n=m} \frac{1}{x_1!x_2!\cdots x_n!} [xm]F(x)=x1+⋯+xn=m∑x1!x2!⋯xn!1
因此答案为:
Answer=m!×[xm]F(x)\text{Answer} = m! \times [x^m] F(x) Answer=m!×[xm]F(x)
四、代码解析与 EGF 对应
核心初始化部分:
for (int i = 1; i <= n; i++) cin >> a[i];
memset(c, 0, sizeof(c));
memset(d, 0, sizeof(d));
for (int i = 0; i <= a[1]; i++) c[i] = inv[i]; // 初始化第一种物品
🔹 含义 1:初始化第一个 EGF
inv[i] = 1 / i!
因此:
c[i]=1i!c[i] = \frac{1}{i!} c[i]=i!1
表示第 1 种物品的生成函数:
f1(x)=∑k=0a1xkk!f_1(x) = \sum_{k=0}^{a_1} \frac{x^k}{k!} f1(x)=k=0∑a1k!xk
接下来的循环:
for (int i=2; i<=n; i++) {for (int j=0; j<=m; j++) {for (int k=0; k<=a[i]; k++) {d[j+k] += c[j] * inv[k];}}for (int j=0; j<=m; j++) c[j]=d[j],d[j]=0;
}
含义 2:EGF 卷积(生成函数乘法)
每次乘入一个新的物品生成函数:
c(x)←c(x)×fi(x)c(x) \leftarrow c(x) \times f_i(x) c(x)←c(x)×fi(x)
展开即:
d[j+k]=∑c[j]⋅1k!d[j+k] = \sum c[j] \cdot \frac{1}{k!} d[j+k]=∑c[j]⋅k!1
表示求 Fi−1(x)⋅fi(x)F_{i-1}(x) \cdot f_i(x)Fi−1(x)⋅fi(x) 的系数卷积。
最终 c[m] 存放的就是 [xm]F(x)[x^m] F(x)[xm]F(x)。
最后一步:
ll res = c[m] * fac[m] % mod;
🔹 含义 3:乘上 m!m!m!
根据公式:
Answer=m!⋅[xm]F(x)\text{Answer} = m! \cdot [x^m] F(x) Answer=m!⋅[xm]F(x)
所以最终结果就是排列总数。
五、数学与代码对应表
| 数学表达式 | 程序变量 / 操作 | 含义 |
|---|---|---|
| fi(x)=∑k=0aixkk!f_i(x) = \sum_{k=0}^{a_i}\frac{x^k}{k!}fi(x)=∑k=0aik!xk | inv[k] | 预存 1/k!1/k!1/k! |
| F(x)=∏fi(x)F(x) = \prod f_i(x)F(x)=∏fi(x) | c[] | 当前生成函数的系数 |
| 卷积:d[j+k]+=c[j]⋅1k!d[j+k]+=c[j]\cdot\frac{1}{k!}d[j+k]+=c[j]⋅k!1 | 双重循环 | 实现乘法 |
| 取系数 [xm]F(x)[x^m]F(x)[xm]F(x) | c[m] | 第 mmm 项系数 |
| 乘 m!m!m! | fac[m]*c[m] | 输出排列数 |
六、EGF 与 OGF 的区别总结
| 对比项 | 普通生成函数 (OGF) | 指数生成函数 (EGF) |
|---|---|---|
| 定义 | F(x)=∑anxnF(x)=\sum a_nx^nF(x)=∑anxn | G(x)=∑anxnn!G(x)=\sum a_n\frac{x^n}{n!}G(x)=∑ann!xn |
| 应用场景 | 组合数(无序选取) | 排列数(有序选取) |
| 系数含义 | 子集数量 | 带顺序排列数 |
| 乘法含义 | 集合并 | 带标号排列 |
七、最终总结
这道题的核心数学思想是:
F(x)=∏i=1n(∑k=0aixkk!)F(x) = \prod_{i=1}^{n}\left(\sum_{k=0}^{a_i}\frac{x^k}{k!}\right) F(x)=i=1∏n(k=0∑aik!xk)
答案为:
Answer=m!⋅[xm]F(x)\boxed{\text{Answer} = m! \cdot [x^m] F(x)} Answer=m!⋅[xm]F(x)
你的代码正是这个公式的实现:
利用数组 c[] 存储 EGF 系数,逐步做卷积,最后乘上 m!m!m!。
题目链接
题解
#include <bits/stdc++.h>
using namespace std;
const int N=1e2+10;
const int mod = 998244353;
typedef long long ll;
int n,m;
ll a[N];
ll c[N],d[N];
ll fac[N];
ll inv[N];
ll qpow(ll a,ll b,ll p)
{ll res = 1;for(;b;b>>=1){if(b&1)res = res*a%p;a = a*a%p;}return res;}void solve()
{for(int i=1;i<=n;i++){cin>>a[i];}memset(c,0,sizeof(c));memset(d,0,sizeof(d));for(int i=0;i<=a[1];i++)c[i]=inv[i];for(int i=2;i<=n;i++){for(int j=0;j<=m;j++){for(int k=0;k<=a[i]&&j+k<=m;k++){d[j+k]+=(c[j]*inv[k])%mod;d[j+k]%=mod;}}for(int j=0;j<=m;j++)c[j]=d[j],d[j]=0;}ll res = c[m]*fac[m]%mod;cout<<res<<endl;}int main()
{ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);int t=1;fac[0]=inv[0]=1;for(int i=1;i<=100;i++){fac[i]=fac[i-1]*i%mod;inv[i]=qpow(fac[i],mod-2,mod);// cout<<fac[i]<<' '<<inv[i]<<endl;}// cin>>t;while(cin>>n>>m){solve();}return 0;
}
