生成函数
基础知识
(1)普通生成函数: k k k 种元素的多重集合的 r r r 组合数(有限和无限多重集都可以)
- 数列:1,1, 1, 1, 1的生成函数,也可以表示一个因子的限制
g ( x ) = 1 + x + x 2 + ⋯ + x n + ⋯ = ∑ n = 0 ∞ x n = 1 1 − x g(x)=1+x+x^2+\dots+x^n+\dots=\sum_{n=0}^{\infty} x^n =\frac 1{1-x} g(x)=1+x+x2+⋯+xn+⋯=n=0∑∞xn=1−x1 - e 1 + e 2 + ⋯ + e k = n e_1+e_2+\dots+e_k=n e1+e2+⋯+ek=n 的正整数解为: C n + k − 1 n C_{n+k-1}^n Cn+k−1n。其生成函数如下
g ( x ) = ∑ n = 0 ∞ C n + k − 1 n x n = 1 ( 1 − x ) k g(x)=\sum_{n=0}^{\infty}C_{n+k-1}^n x^n=\frac {1}{(1-x)^k} g(x)=n=0∑∞Cn+k−1nxn=(1−x)k1
(2)指数生成函数: k k k 种元素的多重集合的 r r r 排列数(有限和无限多重集都可以)
(3)生成函数次数表示选择物品的次数



解题方法
对于直接可计算公式的题目:
例 生成函数为: g ( x ) = ( 1 + x + x 2 + … ) ( 1 + x 2 + x 4 + … ) ( 1 + x 3 + x 6 + … ) … g(x)=(1+x+x^2+\dots)(1+x^2+x^4+\dots)(1+x^3+x^6+\dots)\dots g(x)=(1+x+x2+…)(1+x2+x4+…)(1+x3+x6+…)… 请你计算 x m x^m xm 的系数
解题步骤:
练习题
HDU – 2152 Fruit (普通生成函数)
链接:http://acm.hdu.edu.cn/showproblem.php?pid=2152
题意:有 n 种水果,每种水果出现的次数在 [ a i , b i ] [a_i,b_i] [ai,bi] 之间,问一共买 m 个水果有多少种购买方案?
思路: ( x a 1 + ⋯ + x b 1 ) ( x a 2 + ⋯ + x b 2 ) … ( x a n + ⋯ + x b n ) (x^{a_1}+\dots+x^{b_1})(x^{a_2}+\dots+x^{b_2})\dots(x^{a_n}+\dots+x^{b_n}) (xa1+⋯+xb1)(xa2+⋯+xb2)…(xan+⋯+xbn) 对这个生成函数求 x m x^m xm 的系数
#include <bits/stdc++.h> using namespace std; int n,m; int a[110],b[110],C1[110],C2[110]; int solve() {
for(int i=0;i<=m;++i) C1[i]=C2[i]=0; for(int i=a[1];i<=b[1];++i) C1[i]=1; for(int i=2;i<=n;++i) {
for(int j=0;j<=m;++j) for(int k=a[i];k<=b[i];++k) C2[j+k]+=C1[j]; for(int j=0;j<=m;++j) C1[j]=C2[j],C2[j]=0; } return C1[m]; } int main() {
while(~scanf("%d%d",&n,&m)) {
for(int i=1;i<=n;++i) scanf("%d%d",&a[i],&b[i]); printf("%d\n",solve()); } return 0; }
HDU – 1521 排列组合 (指数生成函数)
链接:http://acm.hdu.edu.cn/showproblem.php?pid=1521
题意:有 n 种物品,每种物品的数量为 a i a_i ai ,问取 m 个物品的排列数
思路: ( 1 + x + x 2 2 ! + ⋯ + x a 1 a 1 ! ) ( 1 + x + x 2 2 ! + ⋯ + x a 2 a 2 ! ) … ( 1 + x + x 2 2 ! + ⋯ + x a n a n ! ) (1+x+\frac {x^2}{2!}+\dots+\frac {x^{a_1}}{a_1!})(1+x+\frac {x^2}{2!}+\dots+\frac {x^{a_2}}{a_2!})\dots(1+x+\frac {x^2}{2!}+\dots+\frac {x^{a_n}}{a_n!}) (1+x+2!x2+⋯+a1!xa1)(1+x+2!x2+⋯+a2!xa2)…(1+x+2!x2+⋯+an!xan) 对这个式子求 x m m ! \frac {x^m}{m!} m!xm 的系数
#include <bits/stdc++.h> using namespace std; int n,m; int a[110],b[110]; double fac[110]; double C1[110],C2[110]; void init() {
fac[0]=fac[1]=1; for(int i=2;i<=100;++i) fac[i]=fac[i-1]*i; } double solve() {
for(int i=0;i<=m;++i) C1[i]=C2[i]=0; for(int i=0;i<=a[1];++i) C1[i]=1.0/fac[i]; for(int i=2;i<=n;++i) {
for(int j=0;j<=m;++j) for(int k=0;k<=a[i];++k) C2[j+k]+=C1[j]*1.0/fac[k]; for(int j=0;j<=m;++j) C1[j]=C2[j],C2[j]=0; } return C1[m]*fac[m]; } int main() {
init(); while(~scanf("%d%d",&n,&m)) {
for(int i=1;i<=n;++i) scanf("%d",&a[i]); printf("%.0lf\n",solve()); } return 0; }
HDU – 1028 Ignatius and the Princess III (普通生成函数)
链接:http://acm.hdu.edu.cn/showproblem.php?pid=1028
题意:将整数 n n n 拆分成正整数相加的形式,问有几种组合
思路:每一个小于 n n n 的正整数都有可能组成。共有 n n n个因子,可以得到生成函数
g ( x ) = ( 1 + x + x 2 + … ) ( 1 + x 2 + x 4 + … ) ( 1 + x 3 + x 6 + … ) … g(x)=(1+x+x^2+\dots)(1+x^2+x^4+\dots)(1+x^3+x^6+\dots)\dots g(x)=(1+x+x2+…)(1+x2+x4+…)(1+x3+x6+…)…
x n x^n xn的系数就是所求的组合方式
#include <bits/stdc++.h> using namespace std; int n,c1[130],c2[130]; int solve(int m) {
for(int i=0; i<=m; ++i) c1[i]=c2[i]=0; for(int i=0; i<=m; ++i) c1[i]=1; for(int i=2; i<=m; ++i) {
for(int j=0; j<=m; ++j) for(int k=0; k<=m&&j+k<=m; k+=i) c2[j+k]+=c1[j]; for(int j=0; j<=m; ++j) c1[j]=c2[j],c2[j]=0; } return c1[m]; } int main() {
while(~scanf("%d",&n)) printf("%d\n",solve(n)); return 0; }
HDU – 1085 Holding Bin-Laden Captive! (普通生成函数)
链接:http://acm.hdu.edu.cn/showproblem.php?pid=1085
题意:由面值为1、2、5的硬币不能组成的最小面值是多少。
思路:可以得到生成函数
g ( x ) = ( 1 + x + x 2 + … ) ( 1 + x 2 + x 4 + … ) ( 1 + x 5 + x 10 ) g(x)=(1+x+x^2+\dots)(1+x^2+x^4+\dots)(1+x^5+x^{10}) g(x)=(1+x+x2+…)(1+x2+x4+…)(1+x5+x10)
从小到大遍历系数,为 0 的那一项就是不能组成的
#include <bits/stdc++.h> using namespace std; int n,a[4],b[4],c1[8005],c2[8005]; void solve(int m) {
for(int i=0; i<=m; ++i) c1[i]=c2[i]=0; for(int i=0; i<=a[1]; ++i) c1[i]=1; for(int i=2; i<=3; ++i) {
for(int j=0; j<=m; ++j) for(int k=0; k<=a[i]*b[i]&&j+k<=m; k+=b[i]) c2[j+k]+=c1[j]; for(int j=0; j<=m; ++j) c1[j]=c2[j],c2[j]=0; } } int main() {
while(scanf("%d%d%d",&a[1],&a[2],&a[3])&&(a[1]||a[2]||a[3])) {
b[2]=2,b[3]=5; int m=a[1]*1+a[2]*2+a[3]*5; solve(m); int x=1; while(x<=m&&c1[x]) x++; printf("%d\n",x); } return 0; }
HDU – 2189 悼念512汶川大地震遇难同胞——来生一起走 (普通生成函数)
链接:http://acm.hdu.edu.cn/showproblem.php?pid=2189
题意:把n个人分成几个小组。每个小组的人数必须是素数
思路:用素数组成n,但是不知道用具体多少个素数。每一个小于等于 n 的素数都是一个因子。
可以得到生成函数:
g ( x ) = ( 1 + x 2 + x 4 + x 6 + … ) ( 1 + x 3 + x 6 + … ) ( 1 + x 5 + x 10 + … ) g(x)=(1+x^2+x^4+x^6+\dots)(1+x^3+x^{6}+\dots)(1+x^5+x^{10}+\dots) g(x)=(1+x2+x4+x6+…)(1+x3+x6+…)(1+x5+x10+…)
x n x^n xn的系数就是这个组合数的答案
BZOJ3028: 食物(普通生成函数 + 推导 + 欧拉降幂)
Description
明明这次又要出去旅游了,和上次不同的是,他这次要去宇宙探险!
我们暂且不讨论他有多么NC,他又幻想了他应该带一些什么东西。理所当然的,你当然要帮他计算携带N件物品的方案数。
他这次又准备带一些受欢迎的食物,如:蜜桃多啦,鸡块啦,承德汉堡等等
当然,他又有一些稀奇古怪的限制:
每种食物的限制如下:
(1)承德汉堡:偶数个
(2)可乐:0个或1个
(3)鸡腿:0个,1个或2个
(4)蜜桃多:奇数个
(5)鸡块:4的倍数个
(6)包子:0个,1个,2个或3个
(7)土豆片炒肉:不超过一个。
(8)面包:3的倍数个
注意,这里我们懒得考虑明明对于带的食物该怎么搭配着吃,也认为每种食物都是以‘个’为单位(反正是幻想嘛),只要总数加起来是N就算一种方案。因此,对于给出的N,你需要计算出方案数,并对10007取模
思路:很明显示生成函数的题,并且考虑的是组合数
可以得到生成函数:
g ( x ) = ( 1 + x 2 + … ) ( 1 + x ) ( 1 + x + x 2 ) ( x + x 3 + … ) ( 1 + x 4 + x 8 + … ) ( 1 + x + x 2 + x 3 ) ( 1 + x ) ( 1 + x 3 + x 6 + … ) g(x)=(1+x^2+\dots )(1+x)(1+x+x^2)(x+x^3+\dots)(1+x^4+x^8+\dots)(1+x+x^2+x^3)(1+x)(1+x^3+x^6+\dots) g(x)=(1+x2+…)(1+x)(1+x+x2)(x+x3+…)(1+x4+x8+…)(1+x+x2+x3)(1+x)(1+x3+x6+…)
g ( x ) = 1 1 − x 2 ( 1 + x ) 1 − x 3 1 − x x 1 1 − x 2 1 1 − x 4 ( 1 − x 4 1 − x ) ( 1 + x ) 1 1 − x 3 g(x)=\frac 1{1-x^2}(1+x)\frac {1-x^3}{1-x}x\frac 1{1-x^2}{\frac {1}{1-x^4}}(\frac {1-x^4}{1-x})(1+x)\frac 1{1-x^3} g(x)=1−x21(1+x)1−x1−x3x1−x211−x41(1−x1−x4)(1+x)1−x31
g ( x ) = x ( 1 − x ) 4 = ∑ n = 0 ∞ C n + 4 − 1 3 x n + 1 = ∑ n = 0 ∞ C n + 2 3 x n g(x)=\frac x{(1-x)^4}=\sum_{n=0}^{\infty}C_{n+4-1}^3x^{n+1}=\sum_{n=0}^{\infty}C_{n+2}^3x^n g(x)=(1−x)4x=n=0∑∞Cn+4−13xn+1=n=0∑∞Cn+23xn
因此,答案就是 a n = C n + 2 3 a_n=C_{n+2}^3 an=Cn+23

#include<bits/stdc++.h> #define ll long long using namespace std; const int Maxn=510; const int mod=10007; char s[Maxn]; ll QuickPower(ll base,ll n) {
ll ret=1; while(n) {
if(n&1) ret=(ret*base)%mod; n>>=1; base=(base*base)%mod; } return ret; } int main() {
scanf("%s",s+1); int n=0,len=strlen(s+1); for(int i=1;i<=len;i++) n=(n*10%mod+s[i]-'0')%mod; ll ans=n*(n+1)%mod*(n+2)%mod*QuickPower(6,mod-2)%mod; printf("%lld\n",ans); }
2019 ICPC上海网络赛 E. Counting Sequences II (指数生成函数 + 推导)
链接:https://nanti.jisuanke.com/t/41413
题意:给定n个位置,对于每一个数 a i a_i ai,都满足 1 ≤ a i ≤ m 1\le a_i \le m 1≤ai≤m,并且如果 a i a_i ai是偶数的话, a i a_i ai出现的次数是偶数次,求排列的个数
思路:求的是排列,所以是指数型生成函数。在[1,m]的范围内,相当于有m个因子,每个因子分别代表对 1 、 2 、 3 、 4 、 … 、 m 1、2、3、4、\dots、m 1、2、3、4、…、m各自的限制。其中偶数有 f l o o r ( m 2 ) floor(\frac m2) floor(2m)个,奇数有 m − f l o o r ( m 2 ) m-floor(\frac m2) m−floor(2m),设 t = f l o o r ( m 2 ) t=floor(\frac m2) t=floor(2m),可以得到指数型生成函数
g ( x ) = ( 1 + x + x 2 2 ! + x 3 3 ! + … ) m − t ( 1 + x 2 2 ! + x 4 4 ! + … ) t g(x)=(1+x+\frac {x^2}{2!}+\frac {x^3}{3!}+\dots)^{m-t}(1+\frac {x^2}{2!}+\frac {x^4}{4!}+\dots)^{t} g(x)=(1+x+2!x2+3!x3+…)m−t(1+2!x2+4!x4+…)t
化简可得:
g ( x ) = e x ( m − t ) ( e x + e − x 2 ) t = e x ( m − t ) ∑ i = 0 t C t i e x ( t − i ) e − x i 2 t g(x)=e^{x(m-t)}(\frac {e^x+e^{-x}}2)^t=\frac{e^{x(m-t)}\sum_{i=0}^tC_t^ie^{x(t-i)}e^{-xi}}{2^t} g(x)=ex(m−t)(2ex+e−x)t=2tex(m−t)∑i=0tCtiex(t−i)e−xi
g ( x ) = ∑ i = 0 t C t i e ( m − 2 i ) x 2 t = ∑ n = 0 ∑ i = 0 t C t i ( m − 2 i ) n 2 t x n n ! g(x)=\frac {\sum_{i=0}^tC_t^ie^{(m-2i)x}}{2^t}=\sum_{n=0}\frac{\sum_{i=0}^tC_t^i{(m-2i)^n}}{2^t}\frac {x^n}{n!} g(x)=2t∑i=0tCtie(m−2i)x=n=0∑2t∑i=0tCti(m−2i)nn!xn
因此可以得到 a n = ∑ i = 0 t C t i ( m − 2 i ) n 2 t a_n=\frac{\sum_{i=0}^tC_t^i{(m-2i)^n}}{2^t} an=2t∑i=0tCti(m−2i)n, a n a_n an就是答案
#include <bits/stdc++.h> #define ll long long using namespace std; const int mod=1e9+7; int t,m; ll n; int qpow(int b,int n,int mod) {
int res=1; while(n) {
if(n&1) res=1ll*res*b%mod; n>>=1; b=1ll*b*b%mod; } return res; } const int N=2e5; int fac[N+10],finv[N+10]; void init() {
fac[0]=fac[1]=1; for(int i=2; i<=N; ++i) fac[i]=1ll*fac[i-1]*i%mod; finv[N]=qpow(fac[N],mod-2,mod); for(int i=N-1; i>=0; --i) finv[i]=1ll*finv[i+1]*(i+1)%mod; } int C(int n,int m) {
return 1ll*fac[n]*finv[m]%mod*finv[n-m]%mod; } int main() {
init(); scanf("%d",&t); while(t--) {
scanf("%lld%d",&n,&m); n%=mod-1; int ans=0; int t=m/2; for(int i=0; i<=t; ++i) ans=(ans+1ll*C(t,i)*qpow(m-2*i,n,mod)%mod)%mod; ll res=qpow(2,t,mod); ans=1ll*ans*qpow(res,mod-2,mod)%mod; printf("%d\n",ans); } return 0; }
发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/230290.html原文链接:https://javaforall.net
