概念
有一序列 a 0 , a 1 , . . . , a n a_0,a_1,...,a_n a0,a1,...,an,那么生成的母函数就是 f ( x ) = a 0 + a 1 x + a 2 x 2 + . . . + a n x n f(x)=a_0+a_1x+a_2x^2+...+a_nx^n f(x)=a0+a1x+a2x2+...+anxn,但是x不在之后的运用中拥有意义,而是借用x前的系数或其上的指数辅助一系列组合问题的求解。
应用场景:
- 一般将需要组合的元素权重用x上的指数表示,不同的数量用其数量组合而成的权重对应的x在同一括号内相加表示,不同权重元素的组合用括号间相乘示。
例如:
现1分邮票、2分邮票、5分邮票分别有3,2,3张,则母函数对应 f ( x ) = ( 1 + x + x 2 + x 3 ) ( 1 + x 2 + x 4 ) ( 1 + x 5 + x 10 + x 15 ) f(x)=(1+x+x^2+x^3)(1+x^2+x^4)(1+x^5+x^{10}+x^{15}) f(x)=(1+x+x2+x3)(1+x2+x4)(1+x5+x10+x15)若要求组成15分的方案数,就可去找 x 15 x^{15} x15的系数,其系数即15分的组合数。
代码实现(以砝码称重为例)
#include<bits/stdc++.h>
#define maxn 1050
#define INF 0x3f3f3f3f
#define ll long long
using namespace std;
int c1[maxn]={0},c2[maxn]={0};
int w[7]={1,2,3,5,10,20};//存储砝码质量
int num[7];//存储不同砝码个数
int main()
{
for(int i=0;i<6;i++)
scanf("%d",&num[i]);
for(int i=0;i<=num[0];i++)
{
c1[i]=1;
c2[i]=0;
}
for(int i=1;i<=5;i++)
{
for(int j=0;j<maxn;j++)
for(int k=0;k<=num[i]*w[i];k+=w[i])
c2[k+j]+=c1[j];
for(int j=0;j<maxn;j++)
{
c1[j]=c2[j];
c2[j]=0;
}
}
ll ans=0;
for(int i=1;i<maxn;i++)
if(c1[i])
ans++;
printf("Total=%lld",ans);//输出能称出的重量个数,也可以c1[n]输出乘除质量为n的方案数
}
- 相关例题:
P8742 [蓝桥杯 2021 省 AB] 砝码称重
思路:天平称重,砝码质量相加或相减的重量都能称出。
代码:
#include<bits/stdc++.h>
#define maxn 150000
#define INF 0x3f3f3f3f
#define ll long long
using namespace std;
int c1[maxn]={0},c2[maxn]={0};
int w[120],N;
int main()
{
scanf("%d",&N);
for(int i=0;i<N;i++)
scanf("%d",&w[i]);
c1[0]=c1[w[0]]=1;
for(int i=1;i<N;i++)
{
for(int j=0;j<=maxn;j++)
for(int k=0;k<=w[i];k+=w[i])
{
c2[k+j]+=c1[j];
if(k>=j)
c2[k-j]+=c1[j];
else
c2[j-k]+=c1[j];
}
for(int j=0;j<maxn;j++)
{
c1[j]=c2[j];
c2[j]=0;
}
}
ll ans=0;
for(int i=1;i<maxn;i++)
if(c1[i])
ans++;
printf("%lld",ans);
}
- 此外每个括号还可表示可重复元素的组合数,因此母函数还可表示n个含有可重复元素的集合抽取m个的组合数
例如:
有红色小球3,蓝色小球2,黄色小球3个,求任取3个小球的组合数。则可用第一个括号内的x指数代表取红色小球的个数情况,第二个括号内的x指数代表取蓝色小球的个数情况,以此类推,最终化简后的母函数对应 x 3 x^3 x3的系数即为答案: f ( x ) = ( 1 + x + x 2 + x 3 ) ( 1 + x + x 2 ) ( 1 + x + x 2 + x 3 ) f(x)=(1+x+x^2+x^3)(1+x+x^2)(1+x+x^2+x^3) f(x)=(1+x+x2+x3)(1+x+x2)(1+x+x2+x3) - 运用母函数的变种——指数型母函数,还可解决部分排列问题,如求在n个含有可重复元素的集合抽取m个的排列数。
大致代码:
const int N=2l;
double c1[N],c2[N];// 注意类型
int val[N],F[N];
void Factorial()
F[0]=1;
//注意初始化
for(int i=1;i <=20; i++){
F[i]=F[i-1]*i;
int main()
int n, m,i,j,k;
//预处理
Factorial();
while(~scanf("%d%d",&n,&m)){
for(i = 0; i < n; ++ i){
scanf("%d",&val[i]);
memset(c1,0,sizeof(c1));
memset(c2,0,sizeof(c2)):
for(i=0;i <= val[0];++i){
c1[i]= 1.0/F[i];
for(i=1; i<n; i++){
for(j=0;j<=m;++j){
for(k=0;k+j<=m && k<=val[i];++k){
c2[j+k]+= c1[j]/F[k];
for(j=0;j<= m; ++j){
cl[j]= c2[j];
c2[j]= 0;}
}
printf("%.0f\n", c1[m]*F[m]);
}
return 0
}
关于指数型母函数:
定义:对于序列
a
0
,
a
1
,
.
.
.
,
a
n
a_0,a_1,...,a_n
a0,a1,...,an,其指数型母函数为
f
(
x
)
=
a
0
+
a
1
1
!
x
+
a
2
2
!
x
2
+
.
.
.
+
a
n
n
!
x
n
+
.
.
.
f(x)=a_0+\frac{a_1}{1!}x+\frac{a_2}{2!}x^2+...+\frac{a_n}{n!}x^n+...
f(x)=a0+1!a1x+2!a2x2+...+n!anxn+...
这样,对应一个集合,其中a重复n1次,b重复n2次,以此类推。从中取r个元素的不同排列数则对应
F
(
x
)
=
(
1
+
1
1
!
x
+
1
2
!
x
2
+
.
.
.
1
n
1
!
x
n
1
)
(
1
+
1
1
!
x
+
1
2
!
x
2
+
.
.
.
1
n
2
!
x
n
2
)
.
.
.
(
.
.
.
)
F(x)=(1+\frac{1}{1!}x+\frac{1}{2!}x^2+...\frac{1}{n_1!}x^{n_1})(1+\frac{1}{1!}x+\frac{1}{2!}x^2+...\frac{1}{n_2!}x^{n_2})...(...)
F(x)=(1+1!1x+2!1x2+...n1!1xn1)(1+1!1x+2!1x2+...n2!1xn2)...(...)化为指数型母函数标准形式后的
a
r
a_r
ar的值。
具体原因可以稍微意会一下,括号之间相乘代表不同情况的组合,而分式下的阶乘则是全排列时对重复情况的剔除,相当于原本计算是先随即抓取一个组合,再全排列剔除重复,而现在则是先提前准备好剔除重复的阶乘,再组合,最后乘以全排列的所有情况,得到
a
r
0
a_{r_0}
ar0,也即答案,先后顺序不影响最终结果。
指数型母函数在数学方面的简化作用:
现有一道经典例题:现在有一长度为N的字符串,满足以下条件:(1) 字符串仅由A,B,C,D四个字母组成;(2) A出现偶数次(也可以不出现);(3)C出现偶数次(也可以不出现);求满足条件的字符串的个数模100的值
如果转化为普通的指数型母函数直接进行求解可以解决小规模的问题,但一旦N的量级过大,系统将无法存储过大的小数位。
- 泰勒展开法:
首先,该题排列数可用 f ( x ) = ( 1 + 1 1 ! x + 1 2 ! x 2 + . . . ) 2 ( 1 + 1 2 ! x 2 + 1 4 ! x 4 + . . . ) 2 f(x)=(1+\frac{1}{1!}x+\frac{1}{2!}x^2+...)^2(1+\frac{1}{2!}x^2+\frac{1}{4!}x^4+...)^2 f(x)=(1+1!1x+2!1x2+...)2(1+2!1x2+4!1x4+...)2母函数进行表示
而根据 e x e^x ex的泰勒展开 e x = 1 + x / 1 ! + x 2 / 2 ! + x 3 / 3 ! + . . e^x=1+x/1!+x^2/2!+x^3/3!+.. ex=1+x/1!+x2/2!+x3/3!+..可得 f ( x ) = ( e x ( e x + e − x ) 2 ) 2 f(x)=(\frac{e^x(e^x+e^{-x})}{2})^2 f(x)=(2ex(ex+e−x))2,化简可得 f ( x ) = 1 4 ( e 4 x + 2 e 2 x + 1 ) f(x)=\frac{1}{4}(e^{4x}+2e^{2x}+1) f(x)=41(e4x+2e2x+1),再次泰勒展开: f ( x ) = ( 1 / 4 ) ∗ ( ( 1 + 4 x / 1 ! + ( 4 x ) 2 / 2 ! + ( 4 x ) 3 / 3 ! + . . . ( 4 x ) N / N ! + 2 ∗ ( 1 + 2 x / 1 ! + ( 2 x ) 2 / 2 ! + ( 2 x ) 3 / 3 ! + . . . ( 2 x ) N / N ! ) + 1 ) f(x)=(1/4)*((1+4x/1!+(4x)^2/2!+(4x)^3/3!+. ..(4x)^N/N!+ 2*(1+2x/1!+(2x)^2/2!+(2x)^3/3!+...(2x)^N/N!) +1) f(x)=(1/4)∗((1+4x/1!+(4x)2/2!+(4x)3/3!+...(4x)N/N!+2∗(1+2x/1!+(2x)2/2!+(2x)3/3!+...(2x)N/N!)+1)
因为答案即为 a N a_N aN,则找相应项 ( 4 x ) N / N ! (4x)^N/N! (4x)N/N!和 2 ∗ ( 2 x ) N / N ! 2*(2x)^N/N! 2∗(2x)N/N!,故只需求得答案 a N = ( 4 N − 1 + 2 N − 1 ) a_N=(4^{N-1}+2^{N-1}) aN=(4N−1+2N−1)即可,则指数快速幂直接拿出答案。
足可见母函数之妙