出自–南昌理工学院ACM集训队
母函数二三认识
什么是母函数?
生成函数即母函数,是组合数学中尤其是计数方面的一个重要理论和工具。 生成函数有普通型生成函数和指数型生成函数两种,其中普通型用的比较多。形式上说,普通型生成函数用于解决多重集的组合问题,而指数型母函数用于解决多重集的排列问题。母函数还可以解决递归数列的通项问题(例如使用母函数解决斐波那契数列的通项公式)。(摘抄自百度)
这里我就说说普通母函数吧(其他的我也不懂 )
母函数的思想很简单—就是把离散数列和幂级数一一对应起来,把离散数列间的相互结合关系对应成为幂级数间的运算关系,最后由幂级数形式来确定离散数列的构造。(还是太玄乎了)
上例题 上才艺
题目:有1克、2克、3克、4克的砝码各一 枚,能称出哪几种重量?每种重量各有几种可能方案?
拿枚举的话,我们来看看枚举的过程,对于每一个砝码来说有拿和不拿两种结果,所以总的结果为2 * 2* 2 * 2=16种
那枚举的结果呢?我们可以看到有:
一种:0g,1g,2g,3g,8g,9g,10g
两种:3g,4g,5g,6g,7g
我们来对前两个砝码的使用情况举个栗子对于一来说:使用1g或者不使用1g
对于二来说:使用2g或者不使用2g
那总的方案数,根据乘法原理来说就可以写成
(使用1g+不使用1g)*(使用2g + 不使用2g)
看到这里我们不禁想说出我们今天要说的内容了 那有没有什么这样一种运算关系,以乘法的形式运算,但是结果表现出类似于加法的关系呢?正好有一个,那就是幂运算。x ^ n 乘上 x ^ m 结果是x ^ m+n,他完美的符合了我们的要求。那么以次数表示砝码的质量, 就可以以多项式的形式表示砝码组合的所有方案。
那我们对上面的方案来说就可以写成(一般由于x ^ 0=1的缘故,所以会写成1+…,但是在这里为了便于理解写成x ^ 0)
(x ^ 0+x ^ 1)*(x ^ 0+x ^ 2)
=x ^ 0 * x ^ 0+x ^ 0 * x ^ 2+x ^ 1 * x ^ 0+x ^ 1*x ^ 2
=x ^ 0+ x ^ 1+x ^ 2+x ^ 3
所以很明显,有四种方案,分别是0g,1g,2g,3g,这与我们的枚举结果一致,我大胆假设一下结果中有相同的项,那么合并同类项后每一项的系数就是这种重量有几种实现方案
接下来我们来写一下三个砝码的过程情况
(x ^ 0+x ^ 1)* (x ^ 0+x ^ 2) * (x ^ 0+x ^ 3)
=(x ^ 0+ x ^ 1+x ^ 2+x ^ 3) * (x ^ 0+x ^ 3)
=x ^ 0+ x ^ 1+x ^ 2+x ^ 3+x ^ 4+x ^ 5
我们在来看四个砝码的情况(才不是因为么有看到系数的变化)
(x ^ 0+x ^ 1)* (x ^ 0+x ^ 2) * (x ^ 0+x ^ 3) *(x ^ 0+x ^ 4)
=(x ^ 0+ x ^ 1+x ^ 2+x ^ 3+x ^ 4+x ^ 5) * (x ^ 0+x ^ 4)
=x ^ 0 + x ^ 1 + x ^ 2 + 2x ^ 3 + 2x ^ 4 + 2x ^ 5 + 2x ^ 6 + 2*x ^ 7 + x ^ 8+ x ^ 9 + x ^ 10
根据枚举的结果我们可以得出我们的结论次数代表组合后可称出的质量,系数代表组合出这种质量的方案的数量。(太难了) 与我们的假设一致,这就是母函数啦(撒花撒花)
接下来我们在把问题升华一下
求用1g、2g、3g的砝码称出不同重量的方案数 。(这是什么鬼,不是和上面的一样的吗?)
对于第一题来说我们1g的只有一个,2g也一样(对每一种的砝码都只有一个),对于第二题来说每一种都是无限多的
于是问题诞生了这样的母函数该怎么写呢?我们来看,因为我们有无数个砝码,就拿1g的砝码和2g的砝码来举例,我们把2个1g的砝码看成是2g的,3个1g砝码看成是3g的,同理2g的砝码,我们可以把2个2g看成是4g的砝码,3个2g看出是6g的砝码,这样就可以推出把m个n g砝码看成是一个n * m g砝码,所以母函数可以写成
(x ^ 0+x ^ 1 + x ^ 2 + x ^ 3 + x ^ 4 + x ^ 5 + …… ) * (x ^ 0 + x ^ 2 + x ^ 4 + x ^ 6 + x ^ 8+ x ^ 10 + ……)
又又又有问题来了,那这不是无限多吗?题目怎么写?这个问题其实很好解决,因为题目自己会限定条件啦
来个栗子
那么如果题目是求用1g、2g、3g的砝码称出10g的方案数 。
表达式就是:
(x ^ 0 +x ^ 1 + x ^ 2 + x ^ 3 + x ^ 4 + x ^ 5 + ……x ^ 10 )*(x0 + x2 + x4 + x6 + x8+ x10 )
结果就是合并同类项后x10的系数。
用1g、2g、3g的砝码称出10g的方案数,本应该有三组表达式,但是1克和2克中已经完全包含3的所有倍数了,可以不写了
#include <iostream>
#include <algorithm>
#include<cstring>
#define endl '\n'
#define IO ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
using namespace std;
typedef long long ll;
const int maxn=1e6+20;
const ll mod = 998244353;
int a[maxn];
inline ll read()
{
ll val = 0, f = 1;
char ch = getchar();
while (ch < '0' || ch > '9')
{
if (ch == '-') f = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9')
{
val = val * 10 + ch - '0';
ch = getchar();
}
return val * f;
}
inline void prints(ll val)
{
if (val < 0)
{
putchar('-');
val = -val;
}
if (val > 9)
prints(val / 10);
putchar(val % 10 + '0');
}
int a1[125],a2[125];
int main()
{
int i,j,k,n;
while(scanf("%d",&n)!=EOF)
{
for(i=0;i<=n;i++)
{
a1[i]=1;
a2[i]=0;
}
for(i=2;i<=n;++i)//求第i个表达式(1+x^(k+i)+x^(k+2i)+x^(k+3i)+...)
{
for(j=0;j<=n;j++)//j 从0到n遍历,这里j就是(前面i个表达式累乘的表达式)里第j个变量,
for(k=0;k+j<=n;k+=i)// k表示的是第j个指数,所以k每次增i(因为第i个表达式的增量是i)。
{
a2[j+k]+=a1[j];
}
// 每次前i个表达式累乘求完后都把中间变量a2赋值给a1
for(j=0;j<=n;++j)
{
a1[j]=a2[j];
a2[j]=0;
}
}
printf("%d\n",a1[n]);
}
return 0;
}