也叫母函数,常用来解决组合方面的问题。
一个常见的例子如下:
有 n 种硬币,每一种硬币的面值为 ,数目为 ,问用这些硬币可以组合出哪些数值的面值,并且各自有多少种组合方法。
这显然是一个背包问题,但是我们考虑用生成函数来解决。一般来说普通的母函数为这样的形式
,系数表示方案数,指数表示方案的状态。对于上面那个问题,可以转化为这样一个多项式乘法:
把它展开之后,每个 x 的指数就表示了组成的面值,系数就表示了方案数。
- 一道例题 Ignatius and the Princess III :给出正整数 N(
),问有多少种本质不同的方案可以使得
,并且满足
。这里本质不同指的是
1 2 1
和1 1 2
算同一种方案。
完全背包肯定是可以做的,生成函数的话可以考虑计算一个这样的式子: ,然后就可以了。
代码:
#define DIN freopen("input.txt","r",stdin);
#define DOUT freopen("output.txt","w",stdout);
#include <bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof(a))
#define REP(i,a,b) for(int i=(a);i<=(int)(b);i++)
#define REP_(i,a,b) for(int i=(a);i>=(b);i--)
#define pb push_back
using namespace std;
typedef long long LL;
typedef vector<int> VI;
typedef pair<int,int> P;
int read()
{
int x=0,flag=1; char c=getchar();
while((c>'9' || c<'0') && c!='-') c=getchar();
if(c=='-') flag=0,c=getchar();
while(c<='9' && c>='0') {x=(x<<3)+(x<<1)+c-'0';c=getchar();}
return flag?x:-x;
}
const int maxn=120;
int f[maxn+5],n,g[maxn+5];
int main()
{
REP(i,0,maxn) f[i]=1;
REP(k,2,maxn)
{
REP(i,0,maxn) for(int j=0;j+i<=maxn;j+=k)
g[j+i]+=f[i];
REP(i,0,maxn) f[i]=g[i],g[i]=0;
}
while(~scanf("%d",&n))
printf("%d\n",f[n]);
return 0;
}
- 再比如说一道稍微复杂一些的题目 Easy ,题意如下:给定三个正整数 N,M 和 K( ),如果找到两个正整数序列 A 和 B,满足 以及 ,那么就可以获得收益 ,问可以获得的总收益是多少?
先不考虑 的收益,只考虑前面的条件限制,我们很容易列出生成函数 ,然后其中 的系数就是方案数,现在我们需要考虑的是对于每个 ,给它赋予一个系数 。因为每个 实际上只是一个方案,如果我们可以把它变成 个方案那就行了。所以把原生成函数转换为 ,这样每个 实际上也可以由 搭配 的某个幂组合出来,所以原来的一个方案就变成了要求的系数。
在 这个生成函数下,我们只需要计算出 的系数,这就是我们需要的答案。分子已经有了 ,所以我们需要计算出 中 的系数,不难看出该式子是三个级数的乘积,对于 中某一项的系数的求法,有如下公式:
根据广义二项式定理,有如下公式:
所以,原问题的答案就是 。
代码:
#define DIN freopen("input.txt","r",stdin);
#define DOUT freopen("output.txt","w",stdout);
#include <bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof(a))
#define REP(i,a,b) for(int i=(a);i<=(int)(b);i++)
#define REP_(i,a,b) for(int i=(a);i>=(b);i--)
#define pb push_back
using namespace std;
typedef long long LL;
typedef vector<int> VI;
typedef pair<int,int> P;
int read()
{
int x=0,flag=1; char c=getchar();
while((c>'9' || c<'0') && c!='-') c=getchar();
if(c=='-') flag=0,c=getchar();
while(c<='9' && c>='0') {x=(x<<3)+(x<<1)+c-'0';c=getchar();}
return flag?x:-x;
}
const int maxn=1e6+5,N=1e6;
const LL M=998244353;
LL inv[maxn],jie[maxn];
LL ksm(LL x,LL n)
{
LL ret=1;
while(n)
{
if(n&1) ret=ret*x%M;
x=x*x%M;
n>>=1;
}
return ret;
}
LL C(LL n,LL m)
{
if(n<0 || m<0 || n<m) return 0;
if(!n || !m) return 1;
return jie[n]*inv[m]%M*inv[n-m]%M;
}
int main()
{
jie[0]=1;
REP(i,1,N) jie[i]=jie[i-1]*i%M;
REP(i,0,N) inv[i]=ksm(jie[i],M-2);
int T=read();
while(T--)
{
int n=read(),m=read(),k=read();
if(n>m) swap(n,m);
LL ans=0;
REP(i,0,n-k)
{
LL x1=C(k+i-1,i);
LL x2=C(k+n-k-i-1,n-k-i);
LL x3=C(k+m-k-i-1,m-k-i);
ans+=x1*x2%M*x3%M;
}
printf("%lld\n",ans%M);
}
return 0;
}