生成函数

一、生成函数基础概念

1.1 普通生成函数(OGF)

普通生成函数的形式为:

\[G(x) = a_0 + a_1x + a_2x^2 + \dots + a_nx^n \]

适用于组合计数问题,如背包问题、整数划分等。

1.2 指数生成函数(EGF)

指数生成函数的形式为:

\[G(x) = a_0 + a_1\frac{x}{1!} + a_2\frac{x^2}{2!} + \dots \]

适用于排列计数问题,如带标号对象的组合。


二、经典应用场景

2.1 递推关系求解

例题1:斐波那契数列通项公式

题目链接斐波那契数列 - 洛谷 P1962

问题描述
已知斐波那契数列的递推式:

\[f(0) = 0, \quad f(1) = 1 \\ f(n) = f(n-1) + f(n-2) \quad (n \geq 2) \]

求斐波那契数列的通项公式。

生成函数解法

  1. 设生成函数:

    \[F(x) = \sum_{n=0}^{\infty} f(n)x^n \]

  2. 根据递推关系建立方程:

    \[F(x) = x + xF(x) + x^2F(x) \]

  3. 解得生成函数的闭合形式:

    \[F(x) = \frac{x}{1 - x - x^2} \]

  4. 对生成函数进行部分分式分解:

    \[F(x) = \frac{1}{\sqrt{5}} \left( \frac{1}{1 - \phi x} - \frac{1}{1 - \psi x} \right) \]

    其中,\(\phi = \frac{1 + \sqrt{5}}{2}\)\(\psi = \frac{1 - \sqrt{5}}{2}\)
  5. 展开生成函数,得到通项公式:

    \[f(n) = \frac{\phi^n - \psi^n}{\sqrt{5}} \]

C++代码实现

#include <iostream>
#include <cmath>
using namespace std;

double fib(int n) {
    double phi = (1 + sqrt(5)) / 2;
    double psi = (1 - sqrt(5)) / 2;
    return (pow(phi, n) - pow(psi, n)) / sqrt(5);
}

int main() {
    int n;
    cin >> n;
    cout << "Fibonacci number F(" << n << ") = " << fib(n) << endl;
    return 0;
}

2.2 组合计数问题

例题2:多重集组合数

题目链接多重集组合数 - 洛谷 P1025

问题描述
有三种物品,分别有无限个、至少取2个、最多取3个,求取5个物品的方案数。

生成函数解法

  1. 构建生成函数:

    \[G(x) = (1 + x + x^2 + \dots)(x^2 + x^3 + \dots)(1 + x + x^2 + x^3) \]

  2. 化简生成函数:

    \[G(x) = \frac{1}{1 - x} \cdot \frac{x^2}{1 - x} \cdot \frac{1 - x^4}{1 - x} \]

  3. 合并得到:

    \[G(x) = \frac{x^2(1 - x^4)}{(1 - x)^3} \]

  4. 展开 \((1 - x)^{-3}\) 使用广义二项式定理:

    \[(1 - x)^{-3} = \sum_{n=0}^{\infty} \binom{n + 2}{2} x^n \]

  5. 计算 \(x^5\) 的系数:

    \[\binom{5 + 2}{2} - \binom{1 + 2}{2} = 21 - 3 = 18 \]

C++代码实现

#include <iostream>
using namespace std;

int main() {
    int n = 5;
    int result = 0;
    for (int a = 0; a <= n; a++) { // 第一种物品
        for (int b = 2; b <= n; b++) { // 第二种物品
            for (int c = 0; c <= 3; c++) { // 第三种物品
                if (a + b + c == n) {
                    result++;
                }
            }
        }
    }
    cout << "Number of ways: " << result << endl;
    return 0;
}

三、例题

例题3:货币系统问题

题目链接货币系统 - 洛谷 P5020

问题描述
给定面值分别为3元、5元、7元的硬币,求组成100元的方案数。

生成函数解法

  1. 构造生成函数:

    \[G(x) = (1 + x^3 + x^6 + \dots)(1 + x^5 + x^{10} + \dots)(1 + x^7 + x^{14} + \dots) \]

  2. 化简生成函数:

    \[G(x) = \frac{1}{(1 - x^3)(1 - x^5)(1 - x^7)} \]

  3. 需要求 \(x^{100}\) 的系数。

优化计算
使用动态规划结合生成函数思想:

#include <iostream>
using namespace std;

const int MOD = 1e9 + 7;

int main() {
    int target = 100;
    int coins[] = {3, 5, 7};
    int dp[101] = {0};
    dp[0] = 1;

    for (int coin : coins) {
        for (int i = coin; i <= target; i++) {
            dp[i] = (dp[i] + dp[i - coin]) % MOD;
        }
    }

    cout << "Number of ways to make 100: " << dp[100] << endl;
    return 0;
}

四、练习题

以下是一些洛谷上的生成函数相关练习题

  1. [P4721] 分治FFT
    题目链接:P4721
    标签:生成函数、FFT、分治

  2. [P4841] 城市规划
    题目链接:P4841
    标签:生成函数、多项式、组合数学

  3. [P5162] WD与积木
    题目链接:P5162
    标签:生成函数、动态规划、多项式

  4. [P5748] 集合划分计数
    题目链接:P5748
    标签:生成函数、多项式、组合数学


posted @ 2025-02-18 19:48  健康铀  阅读(43)  评论(0)    收藏  举报