学习笔记:生成函数II(集合分拆、置换、整数分拆、它们的递推公式、生成函数 和快速计算)

形式幂级数的更多运算

形式幂级数与幂级数的比较

  • 形式幂级数本质是序列(\(x^i\) 无意义),幂级数本质是极限。
  • 形式幂级数通过带入 \(x\) 还原成幂级数。
  • 假设系数在 \(\mathbb{C}\) 上,可以证明形式幂级数与具有正收敛半径的幂级数在 '通常' 的所有运算上服从同样规律(加减乘除求导积分……)。

形式幂级数的更多运算

假设 \(f(x) = a_0 + a_1x + a_2x^2 + \cdots + a_nx^{n} + \cdots\)

求导

定义 \(f(x)\) 的导数为 \(a_1 + 2a_2x + \cdots + (n + 1)a_{n + 1}x^{n} + \cdots\),记做 \(f'(x)\)\(\dfrac{df(x)}{d(x)}\)

对于形式幂级数而言,

求导是序列上的一个变换,即 \(\{a_0, a_1, \cdots, a_n,\cdots\} \Rightarrow \{a_1, 2a_2, \cdots, (n + 1)a_{n + 1},\cdots\}\)

积分

定义 \(f(x)\) 的积分为 \(a_0x + \dfrac{a_1}{2}x^2 + \cdots + \dfrac{a_{n - 1}}{n}x^{n} + \cdots\),记做 \(\int f(x)dx\)\(\int_{0}^{x} f(y)dy\)

\(\{a_0, a_1, \cdots, a_n,\cdots\} \Rightarrow \{0, a_0, \dfrac{a_1}{2}, \cdots, \dfrac{a_{n - 1}}{n},\cdots\}\)

推论:如果 \([x^0]f(x) = 0\),令 \(g(x) = f'(x)\),则 \(\int g(x)dx = f(x)\)

复合

假设 \(f(x) = a_1x + a_2x^2 + \cdots + a_nx^{n} + \cdots, \ g(x) = b_0 + b_1x + b_2x^2 + \cdots + b_nx^{n} + \cdots\)

\(g\) 复合 \(f\) 定义为 \(c_0 + c_1x + \cdots c_nx^n + \cdots\),满足 \(c_0 = b_0, \ c_n = \sum\limits_{k = 1}^{n}b_k\sum\limits_{i_1 + i_2 + \cdots+ i_k = n}\prod a_{i_j}\)

记做 \(g \circ f\)\(g(f(x))\)

如果 \(f\) 的常数项不为 \(0\)

那么 \(c_0 = b_0 + b_0a_0 + b_0a_0^2 + \cdots + b_na_0^n + \cdots\)

\(c_0\) 本身就是一个极限的形式,涉及到收不收敛的问题,这是在形式幂级数中不愿看到的。

因此在定义复合时,一般令 \([x^0]f(x) = 0\)

再考虑 \(c_n = \sum\limits_{k = 1}^{n}b_k\sum\limits_{i_1 + i_2 + \cdots+ i_k = n}\prod a_{i_j}\)

如果 \(i_j > n\),那么 \(\sum{i_j} > n\),所以 \(c_n\) 的定义不需要考虑收敛。

  • \(\exp(x) = \sum_{n \ge0} \dfrac{1}{n!}x^n\)
  • \(\ln (1 + x) = \sum_{n \ge 1}(-1)^{n + 1}\dfrac{x^n}{n}\)
  • \(\ln (1 - x) = -\sum_{n \ge 1}\dfrac{x^n}{n}\)
  • 设形式幂级数 \(f(x)\) 满足 \([x^0]f(x) = 0\),则可定义 \(\exp(f(x))\)\(\ln(1 + (f(x))\)。(或 \([x^0] = 1\) 可定义 \(\ln(f(x))\))。
  • \(g(x) = \exp(f(x)) \iff f(x) = \ln(g(x))\)

推论:\((g \circ f)' = (g' \circ f) \cdot f'\)

假设 \(f(x) = \sum_{i \ge 1} a_ix^i, \ g(x) = \sum_{i \ge0}b_i, \ g(f(x)) = \sum_{i \ge0}c_ix^i\)

满足

\[\begin{cases} f'(x) = \sum_{n \ge 0} (n + 1)a_{n + 1}x^{n} & a_0 = 0\\ \\ g'(x) = \sum_{n \ge 0} (n + 1)b_{n + 1}x^{n}\\ \\ g(f(x))' = \sum_{n \ge 0} (n + 1)c_{n + 1}x^{n} & c_0 = b_0, \ c_n = \sum\limits_{k = 1}^{n}b_k\sum\limits_{i_1 + i_2 + \cdots+ i_k = n}\prod a_{i_j}\\ \\ g'(f(x)) = \sum_{n \ge 0} d_{n}x^{n} & d_n = \sum\limits_{k = 0}^{n}(k + 1) \cdot b_{k + 1}\sum\limits_{i_1 + i_2 + \cdots+ i_k = n}\prod a_{i_j}\\ \end{cases} \]

即证 \((n + 1)c_{n + 1} = \sum\limits_{i = 0}^{n}(i + 1)a_{i + 1}{}d_{n - i}\)

左边:

\[\begin{aligned} \sum\limits_{i = 0}^{n}(i + 1)a_{i + 1}{}d_{n - i} &= \sum\limits_{i = 0}^{n}(i + 1)a_{i + 1}{}\sum\limits_{k = 0}^{n - i}(k + 1) \cdot b_{k + 1}\sum\limits_{i_1 + i_2 + \cdots+ i_k = n - i}\prod a_{i_j}\\ \\ &= \sum\limits_{k = 0}^{n}(k + 1) \cdot b_{k + 1}\sum\limits_{i = 0}^{n}(i + 1)a_{i + 1}{}\sum\limits_{i_1 + i_2 + \cdots+ i_k = n - i}\prod a_{i_j}\\ \\ &= \sum\limits_{k = 1}^{n + 1}k \cdot b_{k}\sum\limits_{i = 1}^{n + 1}i\cdot a_{i}{}\sum\limits_{i_1 + i_2 + \cdots+ i_{k - 1} + i = n + 1}\prod_{j = 1}^{k - 1} a_{i_j}\\ \\ &= \sum\limits_{k = 1}^{n + 1}k \cdot b_{k}\sum\limits_{i_1 + i_2 + \cdots+ i_{k - 1} + i = n + 1}a_{i_1}a_{i_2}\cdots a_{i_{k - 1}}\cdot a_i\cdot i \\ \\ &= \sum\limits_{k = 1}^{n + 1}b_{k}[\sum\limits_{i_1 + i_2 + \cdots+ i_{k - 1} + i_k = n + 1}a_{i_1}\cdots a_{i_k}\cdot i_1 + \sum\limits_{i_1 + i_2 + \cdots+ i_{k - 1} + i_k = n + 1}a_{i_1}\cdots a_{i_k}\cdot i_2 + \sum\cdots ] &\text{把 k 项拆开,每次选出一个特殊的 } i_j\\ \\ &= \sum\limits_{k = 1}^{n + 1}b_{k}\sum\limits_{i_1 + i_2 + \cdots+ i_{k - 1} + i_k = n + 1}(i_1 + i_2 +_ \cdots i_k)\prod_{j = 1}^ka_{i_j}\\ \\ &= \sum\limits_{k = 1}^{n + 1} b_{k}\sum\limits_{i_1 + i_2 + \cdots+ i_{k - 1} + i_k = n + 1}(n + 1)\prod_{j = 1}^ka_{i_j}\\ \end{aligned} \]

右边:

\[\begin{aligned} (n + 1)c_{n + 1} &= (n + 1)\sum\limits_{k =1}^{n + 1}b_k\sum\limits_{i_1 + i_2 + \cdots+ i_k = n + 1}\prod_{i = 1}^ka_{i_j}\\ \end{aligned} \]

左边等于右边。

计算 \(\ln(f(x))\)

(满足 \([x^0]f(x) = 1\))重要观察:

\[\ln(f(x))' = \dfrac{1}{f(x)}f'(x) \]

\(O(n)\) 求导,\(O(n\log n)\) 求逆,\(O(n)\) 积分。

P4725 【模板】多项式对数函数(多项式 ln)

计算 \(\exp(f(x))\)

(满足 \([x^0]f(x) = 1\))如果 \(g(x) = \exp(f(x))\),那么 \(\ln(g(x)) =f(x)\)

构造牛顿迭代 \(h(g(x)) = \ln(g(x)) - f(x)\)

有递推式

\[g(x) = g_0(x) \cdot[1 - \ln(g_0(x)) + f(x)] \pmod{x^{2n}} \]

P4726 【模板】多项式指数函数(多项式 exp)

例题

三轮

题意:\(n\) 种商品,每种商品体积为 \(v_i\),都有 \(10^5\) 件,输出凑成 \(1\)\(m\) 的体积的总方案数。

对质数 \(19260817\) 取模 。(\(n, m, v_i \le 5\times 10^4\)

\(v_i\) 至少为 \(1\),可认为每种商品都有无穷件。

考虑其生成函数 \(F_i(x) = 1 + x^{v_i} + x^{2v_i} + \cdots = \dfrac{1}{1 - x^{v_i}}\)

所以 \(G(x) = \prod\limits_{i = 1}^{k} \dfrac{1}{1 - x^{v_i}}\)

由于 \(v_i\) 很大,无法分治 ntt 求解。

将积化为和,两边取对数。

\[\begin{aligned} \ln(G(x)) &= \sum\limits_{i = 1}^{k} \ln\dfrac{1}{1 - x^{v_i}}\\ \\ &= \sum\limits_{i = 1}^{k} -\ln\ (1 - x^{v_i})\\ \\ &= \sum\limits_{i = 1}^{k} \sum_{j \ge 1} \dfrac{x^{v_ij}}{j} \end{aligned} \]


第二类斯特林数

第二类斯特林数(斯特林子集数)\(\begin{Bmatrix}n\\ k\end{Bmatrix}\),也可记做 \(S_2(n,k)\),表示将 \(n\) 个两两不同的元素,划分为 \(k\) 个互不区分的非空子集的方案数。

递推关系

我们插入一个新元素时,有两种方案:

  • 将新元素放入一个现有的非空子集,有 \(k\begin{Bmatrix}n-1\\ k\end{Bmatrix}\) 种方案。
  • 将新元素单独放入一个子集,有 \(\begin{Bmatrix}n-1\\ k-1\end{Bmatrix}\) 种方案;

\[\begin{Bmatrix}n\\ k\end{Bmatrix}=\begin{Bmatrix}n-1\\ k-1\end{Bmatrix}+k\begin{Bmatrix}n-1\\ k\end{Bmatrix} \]

边界是 \(\begin{Bmatrix}n\\ 0\end{Bmatrix}=[n=0]\)

通项公式

不妨先认为 \(k\) 个集合互不相同,转化成经典容斥问题,最后乘上 \(\dfrac{1}{k!}\)

\[\begin{Bmatrix}n\\ k\end{Bmatrix} = \dfrac{1}{k!}\sum\limits_{i = 0}^k (-1)^i C_k^i (k - i)^n \]

重要公式

记下降阶乘幂 \(x^{\underline{n}}=\dfrac{x!}{(x-n)!}=\prod_{k=0}^{n-1} (x-k)\)

则可以利用下面的恒等式将普通幂转化为下降幂:

\[x^n =\sum\limits_{k = 0}^n \begin{Bmatrix}n\\ k\end{Bmatrix} \begin{pmatrix}x\\ k\end{pmatrix} k! =\sum\limits_{k = 0}^n \begin{Bmatrix}n\\ k\end{Bmatrix} x^{\underline{k}} \]

考虑各式组合意义。

  • \(x^n \rightarrow\) \(n\) 个不同的球放入 \(x\) 个不同的盒子。
  • \(\begin{pmatrix}x\\ k\end{pmatrix} \rightarrow\) \(x\) 个盒子中选出 \(k\) 个。
  • \(\begin{Bmatrix}n\\ k\end{Bmatrix} \rightarrow\) \(n\) 个不同的球放入 \(k\) 个相同的盒子且都不为空。
  • \(k! \rightarrow\) 将选出的 \(k\) 个盒子排列。

正确性显然。

指数生成函数

\[\sum_{n \ge 0} \begin{Bmatrix}n\\k\end{Bmatrix}\dfrac{x^n}{n!} = \dfrac{1}{k!}(\exp(x) - 1)^k \]

不妨将盒子染成 \(k\) 种不同颜色,每种颜色在长度为 \(n\) 的排列中必须出现一次。

对于第一种颜色,\(F_1(x) = x + \dfrac{x^2}{2!} + \dfrac{x^3}{3!} + \cdots = \exp(x) - 1\)(没有零次项,因为必须出现)。

全部的合法方案为 \([x^n]\prod F_i\)

最后将染色去除,得到 \(G(x) = \dfrac{1}{k!}(\exp(x) - 1)^k\)

第二类斯特林数·列

利用公式

\[\sum_{n \ge 0} \begin{Bmatrix}n\\k\end{Bmatrix}\dfrac{x^n}{n!} = \dfrac{1}{k!}(\exp(x) - 1)^k \]

其中

\[f(x)^k = \exp(\ln f(x)^k) = \exp(k\ln f(x)) \]

如果 \([x^0]f(x) \neq 1\) 则不能直接求对。

考虑给每项除以一个公因式 \(a_ix^i\),满足第 \(i\) 项是系数非 \(0\) 的最低项。

最后再乘上 \((a_ix^i)^k\)

P5396 第二类斯特林数·列submission

EGF 求得的系数为 \(\dfrac{a_n}{n!}\)

第二类斯特林数·行

\[\begin{aligned} \begin{Bmatrix}n\\k\end{Bmatrix} &= \dfrac{1}{k!}\sum_{i = 0}^k(-1)^i\begin{pmatrix}k\\i\end{pmatrix}(k - i)^n\\ \\ &= \sum_{i = 0}^k\dfrac{(-1)^i}{i!}\dfrac{(k - i)^n}{(k - i)!}\\ \end{aligned} \]

\(F(x) = \sum_{i \ge 0} \dfrac{(-1)^i}{i!}x^i \,, \ G(x) = \sum_{i \ge 0} \dfrac{i^n}{i!}x^i\)

于是 \(S_2(n) = F * G\)

P5395 第二类斯特林数·行submission


第一类斯特林数

\(k\) 个轮换的 \(n\) 元置换的方案,记为 \(c(n, k)\)\(\begin{bmatrix}n\\k\end{bmatrix}\)

置换向原位置连边,形成若干个环,即轮换。

对于置换 \(\sigma = \begin{pmatrix}1 & 2 & 3 & 4 & 5\\3 & 5 & 1 & 2 & 4\end{pmatrix}\),有两个轮换:

递推关系

\[\begin{bmatrix}n\\k\end{bmatrix} = \begin{bmatrix}n - 1\\k - 1\end{bmatrix} + (n - 1)\begin{bmatrix}n - 1\\k\end{bmatrix} \]

边界:\(\begin{bmatrix}0\\0\end{bmatrix} = \begin{bmatrix}1\\1\end{bmatrix} = 1\)

  • 自己向自己连边,单独成环。

  • 已经有了 \(k\) 个环,在 \(n - 1\) 个空隙里选一个插入。

重要公式

上升幂转普通幂

\[x^{\overline{n}} = \sum_{k = 0}^n\begin{bmatrix}n\\k\end{bmatrix}x^k \]

假设 \(x^{\overline{n}} = \sum\limits_{k = 0}^nf(n, k)x^k\)

联立

\[\begin{cases} x^{\overline{n}} = \sum\limits_{k = 0}^nf(n, k)x^k\\ \\ x^{\overline{n}} = (x + n - 1)x^{\overline{n - 1}} = \sum\limits_{k = 0}^{n - 1}f(n - 1, k)\cdot (x + n - 1) \cdot x^k\\ \end{cases} \]

得到 \(f\) 的递推式:\(f(n, k) = f(n - 1, k - 1) + (n - 1)\cdot f(n - 1, k)\)

发现 \(f\) 与第一类斯特林数递推式相同,初值相同,证毕。

下降幂转普通幂

\[x^{\underline{n}} = \sum_{k = 0}^n(-1)^{n + k}\begin{bmatrix}n\\k\end{bmatrix}x^k \]

\(-x\) 带入 \(1\) 中的 \(x\)

\[\begin{aligned} (-x)^{\overline{n}} &= (-x)(1 - x)\cdots(n - 1 - x)\\ \\ &= (-1)^n\cdot x(x - 1)\cdots(x - n + 1)\\ \\ &= (-1)^n\cdot x^{\underline {n}} = \sum_{k = 0}^n\begin{bmatrix}n\\k\end{bmatrix}(-x)^k\\ \end{aligned} \]

把左式的负号移至右式,即证。

指数生成函数

等效于 \(n\) 个不同球放入 \(k\) 个互不区分且非空的盒子,装有 \(i\) 个球的盒子有 \((i - 1)!\) 种不同形态,求方案数。

\(i\) 个球只用大小为 \(i\) 的盒子的方案为 \((i - 1)!\)

先将盒子编号,对于每个盒子,有生成函数 \(F(x) = x + \dfrac{x^2}{2!} + 2!\cdot\dfrac{x^3}{3!} + \cdots = -\ln(1 - x)\)

\(k\) 个盒子,则 \(G(x) = \dfrac{1}{k!}[-\ln(1 - x)]^k\)

第一类斯特林数·列

\[\begin{bmatrix}n\\k\end{bmatrix}= n! \cdot [x^n]G(x) \]

P5409 第一类斯特林数·列submission

第一类斯特林数·行

展开

\[x^{\overline{n}} = \sum_{k = 0}^n\begin{bmatrix}n\\k\end{bmatrix}x^k \]

显然可以分治 ntt \(O(n\log^2n)\) 求(但被出题人卡了)。

考虑倍增。

\[x^{\overline{2n}} = x^\overline{n}(x + n)^\overline{n} \]

\(f(x) = \prod\limits_{i = 0}^{n - 1}(x + i) = \sum\limits_{i = 0}^{n}a_ix^i\),则 \((x + n)^\overline{n} = f(x + n)\)

问题转化为已知多项式 \(f(x)\),快速求 \(f(x + c)\)

\[\begin{aligned} \sum\limits_{i = 0}^{n}a_i(x + c)^i &= \sum\limits_{i = 0}^{n}a_i\sum_{j = 0}^i\begin{pmatrix}i\\j\end{pmatrix}x^j \cdot c^{i - j}\\ \\ &= \sum_{j = 0}^nx^j \sum_{i = j}^{n}\begin{pmatrix}i\\j\end{pmatrix}a_i\cdot c^{i - j}\\ \\ &= \sum_{j = 0}^n\dfrac{x^j}{j!} \sum_{i = j}^{n}a_i\cdot i!\cdot \dfrac{c^{i - j}}{(i - j)!}\\ \end{aligned} \]

第二个求和式可以写成 \(g(x) = \sum i!\cdot a_ix^{i}, \ h(x) = \sum \dfrac{c^i}{i!}x^{n - i}\) 的卷积。

\([x^j]f(x + c) = [x^{j + n}]g*h\)

时间复杂度 \(T(n) = T(n / 2) + O(n\log n) = O(n\log n)\)

submission

有符号的第一类斯特林数

定义:\(S_1(n, k) = (-1)^{n + k}\begin{bmatrix}n\\k\end{bmatrix}\)

重要公式

\[x^{\underline{n}} = \sum_{k = 0}^nS_1(n, k)x^k \]

指数生成函数

\[\sum_{i \ge 0}S_1(n, k)\dfrac{x^n}{n!} = \dfrac{1}{k!}[\ln(1 + x)]^k \]

两类斯特林数的关系及斯特林反演

\[\begin{aligned} \sum_{k \ge 0} (-1)^{n + k}\begin{bmatrix}n\\k\end{bmatrix}\begin{Bmatrix}k\\m\end{Bmatrix} = [n = m]\\ \\ \sum_{k \ge 0} (-1)^{k + m}\begin{Bmatrix}n\\k\end{Bmatrix}\begin{bmatrix}k\\m\end{bmatrix} = [n = m]\\ \end{aligned} \]

证明:

对于公式 \(x^{n} = \sum\limits_{k = 0}^n\begin{Bmatrix}n\\k\end{Bmatrix}x^\underline{k}\),可以写成向量之积:

\[\begin{pmatrix} x^0\\ x^1\\ \vdots\\ x^n\\ \vdots\\ \end{pmatrix} = \begin{pmatrix} s_2(0, 0)\\ s_2(1, 0) & s_2(1, 1)\\ \vdots\\ s_2(n, 0) & s_2(n, 1) & s_2(n, 2) & \cdots & s_2(n, n)\\ \vdots \end{pmatrix} \begin{pmatrix} x^\underline0\\ x^\underline1\\ \vdots\\ x^\underline n\\ \vdots\\ \end{pmatrix} \]

对于 有符号 第一类斯特林数:

\[\begin{pmatrix} x^\underline0\\ x^\underline1\\ \vdots\\ x^\underline n\\ \vdots\\ \end{pmatrix} = \begin{pmatrix} s_1(0, 0)\\ s_1(1, 0) & s_1(1, 1)\\ \vdots\\ s_1(n, 0) & s_1(n, 1) & s_1(n, 2) & \cdots & s_1(n, n)\\ \vdots \end{pmatrix} \begin{pmatrix} x^0\\ x^1\\ \vdots\\ x^n\\ \vdots\\ \end{pmatrix} \]

我们有 \(S_1\times S_2 = I\)

则在 \(S_1\) 中取一行 \(n\),在 \(S_2\) 中取一列 \(m\),两个向量之积为 \(1\) 当且仅当 \(m = n\)

\(\sum_{k \ge 0} (-1)^{n + k}\begin{bmatrix}n\\k\end{bmatrix}\begin{Bmatrix}k\\m\end{Bmatrix} = [n = m]\)

斯特林反演

\[f_n = \sum_{i = 0}^n\begin{Bmatrix}n\\i\end{Bmatrix}g_i \iff g_n = \sum_{i = 0}^n(-1)^{n + i}\begin{bmatrix}n\\i\end{bmatrix}f_i \]

证明:

\[\begin{aligned} f_n &= \sum_{i = 0}^n[i = n]f_i\\ \\ &= \sum_{i = 0}^n\sum_{k \ge 0}^n (-1)^{k + i}\begin{Bmatrix}n\\k\end{Bmatrix}\begin{bmatrix}k\\i\end{bmatrix}f_i\\ \\ &= \sum_{k \ge 0}^n\begin{Bmatrix}n\\k\end{Bmatrix}\sum_{i = 0}^n (-1)^{k + i}\begin{bmatrix}k\\i\end{bmatrix}f_i\\ \\ &= \sum_{k \ge 0}^n\begin{Bmatrix}n\\k\end{Bmatrix}g_k \end{aligned} \]


整数拆分

\(n\)\(k\) 无序拆分

\(n\) 个无标号球分配到 \(k\) 个无标号盒子的方案数,满足每个盒子不为空,记为 \(p(n, k)\)

递推关系

\[p(n, k) = p(n - 1, k - 1) + p(n - k, k) \]

假设第 \(i\) 个盒子(第 \(i\) 行)里有 \(a_i\) 个球,满足 \(a_1 \ge a_2\cdots\ge a_k \ge 1\)

分两部分考虑。

  • \(k\) 个盒子有且仅有新加入的一个球,方案为 \(p(n - 1, k - 1)\)
  • \(k\) 个盒子不止一个球,把黄色一列去掉,方案为 \(p(n - k, k)\)

常生成函数

考虑上图中有多少有 \(i\) 个球的列。

\(i\) 个球时,只能有 \(1\) 列,一种方案。

\(2i\) 个球时,只能有 \(2\) 列,一种方案。

因此其常生成函数为 \(F_i(x) = 1 + x^{i} + x^{2i} + \cdots = \dfrac{1}{1 - x^i}\)

\(G_k = \prod_{i = 1}^kF_i\)

由于至少要有一列为 \(k\),所以

\[P_k(x) = G_k(x) - G_{k - 1}(x) = x^k\prod\limits_{i = 1}^k\dfrac{1}{1 - x^i} \]

  • 快速求 \(P_k\) 的前 \(n\) 项:对后式先取 \(\ln\)\(\exp\),详见 形式幂级数的更多运算-例题-三轮

    \[\sum\limits_{i = 1}^{k} \ln\dfrac{1}{1 - x^{i}} = \sum\limits_{i = 1}^{k} \sum_{j \ge 1} \dfrac{x^{ij}}{j} \]

\(n\) 的无序拆分

\(n\) 个无标号球分配到一些无标号盒子的方案数,满足每个盒子不为空,记为 \(p(n)\)

可知 \(p(n) = \sum_{k = 1}^np(n, k)\)

常生成函数

延续上板的思考方向,行的个数没有限制,所以只要一些长度为 \(i\) 的凑出 \(n\) 个球即可。

那么

\[\begin{aligned} P(x) &=\prod_{i \ge 1}\dfrac{1}{1 - x^i}\\ \\ &= \exp\sum_{i \ge1}\sum_{j \ge 1}\dfrac{x^{ij}}{j}\\ \\ &= \exp\sum_{n \ge1}\sum_{d \mid n}\dfrac{x^{n}}{d}\\ \end{aligned} \]

LOJ6268. 分拆数submission

递推关系

证明繁琐,当结论记。

\[p(n) = \sum_{k \ge 1}(-1)^{k - 1}[p(n - \dfrac{3k^2 - k}{2}) + p(n - \dfrac{3k^2 + k}{2})] \]

产生贡献的项只有 \(O(\sqrt N)\) 个,复杂度 \(O(N\sqrt N)\)

主要运用于模数不友好的情况。


生成函数模型

分配问题总结

\(n\) 个球放入 \(k\) 个盒子,球方案数。

\[\begin{array}{|c|*{3}{l|}} \hline n\text{ 个球} & k\text{ 个盒子} & \text{盒子可以为空} & \text{每个盒子至少一个球}\\ \hline \\ \text{有标号} & \text{有标号} & k^n & k!\begin{Bmatrix}n\\k\end{Bmatrix}\\ \hline \\ \text{有标号} & \text{无标号} & \sum\limits_{i = 1}^k\begin{Bmatrix}n\\k\end{Bmatrix} & \begin{Bmatrix}n\\k\end{Bmatrix}\\ \hline \\ \text{无标号} & \text{有标号} & \begin{pmatrix}n + k - 1\\k - 1\end{pmatrix} & \begin{pmatrix}n - 1\\k - 1\end{pmatrix}\\ \hline \\ \text{无标号} & \text{无标号} & p(n + k, k) & p(n, k)\\ \hline \end{array} \]

p(n + k, k):把每行之前都加一个球,则 \(k\) 个盒子都不为空

分配问题(加强版1)

\(n\) 个球放入 \(k\) 个盒子,装有 \(i\) 个球的盒子有 \(f_i\) 种形态,不同形态算不同方案,有多少种方案?

\(\{f_i\}_{i\ge1}\) 的常生成函数 \(F(x) = \sum_{i\ge1}f_ix^i\),其指数生成函数 \(E(x) = \sum_{i \ge 1}\dfrac{f_i}{i!}x^i\)(有没有常数项取决于需不需要每个盒子都有球)。

\[\begin{array}{|c|*{3}{l|}} \hline n\text{ 个球} & k\text{ 个盒子} & 方案的生成函数\\ \hline \\ \text{有标号} & \text{有标号} & \text{e.g.f} = E(x)^k\\ \hline \\ \text{有标号} & \text{无标号} & \text{e.g.f} = \dfrac{1}{k!}E(x)^k\\ \hline \\ \text{无标号} & \text{有标号} & \text{o.g.f} = F(x)^k\\ \hline \end{array} \]

分配问题(加强版2)

\(n\) 个球放入一些盒子且每个盒子都不为空,装有 \(i\) 个球的盒子有 \(f_i\) 种形态,不同形态算不同方案,有多少种方案?

\(\{f_i\}_{i\ge1}\) 的常生成函数 \(F(x) = \sum_{i\ge1}f_ix^i\),其指数生成函数 \(E(x) = \sum_{i \ge 1}\dfrac{f_i}{i!}x^i\)

即对加强版 \(1\) 做个 \(\sum_{i\ge0}\)

\[\begin{array}{|c|*{3}{l|}} \hline n\text{ 个球} & k\text{ 个盒子} & 方案的生成函数\\ \hline \text{有标号} & \text{有标号} & \text{e.g.f} = \dfrac{1}{1 - E(x)^k}\\ \hline \\ \text{有标号} & \text{无标号} & \text{e.g.f} = \exp E(x)^k\\ \hline \\ \text{无标号} & \text{有标号} & \text{o.g.f} = \dfrac{1}{1 - F(x)^k}\\ \hline \text{无标号} & \text{无标号} & \text{o.g.f} = \prod_{i \ge 1}(\dfrac{1}{1 - x^i})^{f_i} = \exp\sum_{j \ge 1}\dfrac{1}{j}F(x^j) \\ \hline \end{array} \]

无标号/无标号:

考虑大小为 \(i\) 的盒子的常生成函数(即 \([x^n]\) 表示只用大小为 \(i\) 的盒子放入 \(n\) 个球的方案)。

对于盒子的第一种形态 \(g(x) = 1 + x^i + x^{2i} + \cdots\)

以此类推,其常生成函数为 \(g^{f_i - 1}(x)\)

所以无标号/无标号的常生成函数为

\[\prod_{i \ge 1}(\dfrac{1}{1 - x^i})^{f_i} = \exp\sum_{i \ge 1} f_i\sum_{j \ge 1}\dfrac{x^{ij}}{j} = \exp\sum_{j \ge 1}\dfrac{1}{j}\sum_{i \ge 1} f_ix^{ij} = \exp\sum_{j \ge 1}\dfrac{1}{j}F(x^j) \]

例题

CF961G Partitions

题意:

给出 \(n\) 个物品,每个物品有一个权值 \(w_i\)

定义一个集合 \(S\) 的权值 \(W(S)=|S|\sum\limits_{x\in S}w_x\)

定义一个划分的权值为 \(W'(R)=\sum\limits_{S\in R}W(S)\)

求将 \(n\) 个物品划分成 \(k\) 个集合的所有方案的权值和。答案对 \(10^9+7\) 取模。

\(n,k\le2\times10^5\)\(w_i\le10^9\)


感性理解,每个数的贡献是相同的,所以答案一定是 \(P\cdot \sum a_i\) 的形式。

枚举当前元素所在集合大小。

\[\begin{aligned} P &= \sum_{i = 1}^ni\cdot \begin{pmatrix}n - 1\\i - 1\end{pmatrix}\begin{Bmatrix}n - i\\k - 1\end{Bmatrix} &\text{从剩下 } n - 1 \text{ 个里选出在当前集合的,再划分其余的 }n - i \text{个。}\\ \\ &= \sum_{i = 1}^ni\cdot \dfrac{(n - 1)!}{(i - 1)!(n - i)!}\sum_{j = 0}^{k - 1}(-1)^j\dfrac{(k - j - 1)^{n - i}}{j!(k - j - 1)!}\\ \\ &= \sum_{j = 0}^{k - 1}(-1)^j\dfrac{1}{j!(k - j - 1)!}\sum_{i = 1}^ni\cdot \dfrac{(n - 1)!(k - j - 1)^{n - i}}{(i - 1)!(n - i)!}\\ \end{aligned} \]

单独计算后面一个求和式,把 \(k - j - 1\) 当作常数 \(t\)

\[\begin{aligned} \sum_{i = 1}^ni\cdot \dfrac{(n - 1)!t^{n - i}}{(i - 1)!(n - i)!} &=\sum_{i = 1}^n\dfrac{(n - 1)!t^{n - i}}{(i - 1)!(n - i)!}\cdot (i + 1 - 1)\\ \\ &=\sum_{i = 1}^n\dfrac{(n - 1)!t^{n - i}}{(i - 1)!(n - i)!}\cdot (i - 1 + 1)\\ \\ &=(n- 1)\sum_{i = 1}^n\dfrac{(n - 2)!}{(i - 2)!(n - i)!}\cdot t^{n - i} + \sum_{i = 1}^n\dfrac{(n - 1)!}{(i - 1)!(n - i)!}\cdot t^{n - i}\\ \\ &=(n- 1)\sum_{i = 1}^n\begin{pmatrix}n - 2\\n- i\end{pmatrix}\cdot t^{n - i} + \sum_{i = 1}^n\begin{pmatrix}n - 1\\n- i\end{pmatrix}\cdot t^{n - i}\\ \\ &=(n- 1)(t + 1)^{n - 2} + (t + 1)^{n - 1} \end{aligned} \]

时间复杂度 \(O(n\log n)\)

还有一种更为精巧的解法:

考虑 \(w_i\) 会对答案产生多少次贡献?

在一个大小为 \(|S|\) 的集合中,\(w_i\) 贡献 \(|S|\) 次,等效于集合中每一个元素都使 \(w_i\) 产生一次贡献。

分两部分讨论

  • 自己使自己的产生的贡献,显然每一种划分产生一次,共 \(\begin{Bmatrix}n\\k\end{Bmatrix}\) 次。

  • 其他元素使自己的产生的贡献,把其余 \(n - 1\) 个球划分好 \(k\) 个集合,对于每种划分,当前元素可以任选一个集合放进去,因此 \(n - 1\) 个物品都会有一次贡献,共 \((n - 1)\begin{Bmatrix}n - 1\\k\end{Bmatrix}\)

综上 \(P = \begin{Bmatrix}n\\k\end{Bmatrix} + (n - 1)\begin{Bmatrix}n-1\\k\end{Bmatrix}\)

submission

CF960G Bandit Blues

题意:给定 \(n, \ a, \ b\),定义 \(A\) 为一个排列中是前缀最大值的数的个数,定义 \(B\) 为一个排列中是后缀最大值的数的个数,求长度为 \(n\) 的排列中满足 \(A = a\)\(B = b\) 的排列个数。\(n \le 10^5\),对 \(998244353\) 取模。

首先,排列里最大值一定同时是前缀最大和后缀最大,如果存在 \(a/b = 0\),无解。

\(f(i, j)\) 表示 \(i\) 个数的排列有 \(j\) 个前缀最大的方案。

不妨从大到小一个一个填。

  • 填到序列的最前面,一定是前缀最大,有 \(f(i - 1, j - 1)\) 种情况。
  • 由于所有数都比他大,填到任意一个数后面,有 \((i - 1)f(i - 1, j)\) 种情况。

所以 \(f(i, j) = f(i - 1, j - 1) + (n - 1) f(i - 1, j)\),恰为第一类斯特林数的递推式,由于边界相同,所以 \(f(i, j) = \begin{bmatrix}i\\j\end{bmatrix}\)

枚举最大值左边的元素个数。

则答案为 \(\sum\limits_{i = 0}^{n - 1}\begin{pmatrix}n - 1\\i\end{pmatrix}\begin{bmatrix}i\\a - 1\end{bmatrix}\begin{bmatrix}n - i - 1\\b - 1\end{bmatrix}\)

可以直接求出两列斯特林数,但形式仍不够优美。

考虑式子的组合意义:选出 \(i\) 个排成 \(a - 1\) 个环 \(\rightarrow\) 剩下 \(n - 1 - i\) 个排成 \(b - 1\) 个环。

也就是 \(n - 1\) 个元素排成 \(a + b - 2\) 个环,选其中 \(a - 1\) 个分给左边。

于是答案化简为 \(\begin{bmatrix}n - 1\\a + b- 2\end{bmatrix}\begin{pmatrix}a + b - 2\\a - 1 \end{pmatrix}\)

第一列斯特林数没有实用的通项公式,随便求出一行或一列即可。

submission

附录:模板

#include<bits/stdc++.h>
using namespace std;

using ll = long long;
constexpr int N = 5e5 + 5, P = 998244353;		// g = 3

ll gg[30], ig[30], fac[N], ifac[N], inv[N];

ll qpow(ll a, ll b) {
	ll ret = 1;
	while(b) {
		if(b & 1) ret = ret * a % P;
		b >>= 1;
		a = a * a % P;
	}
	return ret;
}

void init() {
	gg[0] = ig[0] = 1;
	for(int i = 1; i < 30; ++ i) gg[i] = qpow(3, (P - 1) / (1 << i));
	for(int i = 1; i < 30; ++ i) ig[i] = qpow(gg[i], P - 2);
	fac[0] = 1;
	for(int i = 1; i < N; ++ i) {
		inv[i] = (i == 1) ? 1 : -(P / i) * inv[P % i] % P;
		fac[i] = fac[i - 1] * i % P;
	}
	ifac[N - 1] = qpow(fac[N - 1], P - 2);
	for(int i = N - 1; i >= 1; -- i) {
		ifac[i - 1] = ifac[i] * i % P; 
	}
}

int rev[N];

void ntt(ll *a, int tot, int ty) {
	for(int i = 0; i < tot; ++ i) {
		if(i < rev[i]) {
			swap(a[i], a[rev[i]]);
		}
	}
	int t = 1;
	for(int mid = 1; mid < tot; mid <<= 1, ++ t) {
		ll g1 = gg[t];
		if(ty == -1) {
			g1 = ig[t];
		}
		for(int i = 0; i < tot; i += mid * 2) {
			ll gk = 1;
			for(int j = 0; j < mid; ++ j, gk = gk * g1 % P) {
				ll x = a[i + j], y = a[i + j + mid];
				a[i + j] = (x + gk * y) % P;
				a[i + j + mid] = (x - gk * y) % P;
			}
		}
	}
	if(ty == -1) {
		ll tmp = qpow(tot, P - 2);
		for(int i = 0; i < tot; ++ i) {
			a[i] = a[i] * tmp % P;
		}
	}
}


void polymul(ll *f, const ll *g, const ll *h, int n, int m) {		// 项数,非次数
	static ll a[N], b[N];
	int tot = 1, bit = 0;
	while(tot < n + m - 1) ++ bit, tot <<= 1;
	for(int i = 0; i < tot; ++ i) rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << bit - 1);
	memcpy(a, g, n * 8), memset(a + n, 0, (tot - n) * 8);
	memcpy(b, h, m * 8), memset(b + m, 0, (tot - m) * 8);
	ntt(a, tot, 1), ntt(b, tot, 1);
	for(int i = 0; i < tot; ++ i) a[i] = a[i] * b[i] % P;
	ntt(a, tot, -1);
	memcpy(f, a, tot * 8);
}

void polyinv(ll *f, const ll *h, int n){
	static ll d[N], g[N];
	int V = 1; while(V < n + n - 1) V <<= 1;
	memcpy(d, h, n * 8), memset(d + n, 0, (V - n) * 8);
	memset(f, 0, V * 8), memset(g, 0, V * 8);
	f[0] = qpow(h[0], P - 2);
	for(int w = 2; w / 2 < n; w <<= 1){
		memcpy(g, d, w * 8);
		for(int i = 0; i < w * 2; ++i) rev[i] = (rev[i >> 1] >> 1) | (i & 1 ? w : 0);
		ntt(f, w << 1, 1), ntt(g, w << 1, 1);
		for(int i = 0; i < w * 2; ++i) f[i] = (2 - f[i] * g[i]) % P * f[i] % P;
		ntt(f, w << 1, -1);
		memset(f + w, 0, w * 8);
	}
	memset(f + n, 0, (V - n) * 8);
}


void polysqrt(ll *f, const ll *h, int n){
	static ll d[N], g[N], f_inv[N];
	int V = 1; while(V < n + n - 1) V <<= 1;
	memcpy(d, h, n * 8), memset(d + n, 0, (V - n) * 8);
	memset(f, 0, V * 8), memset(g, 0, V * 8), memset(f_inv, 0, V * 8);
	f[0] = 1;
	constexpr int i2 = 499122177;
	for(int w = 2; w / 2 < n; w <<= 1){
		memcpy(g, d, w * 8);
		for(int i = 0; i < w * 2; ++i) rev[i] = (rev[i >> 1] >> 1) | (i & 1 ? w : 0);
		polyinv(f_inv, f, w);
		ntt(f, w << 1, 1), ntt(g, w << 1, 1), ntt(f_inv, w << 1, 1);
		for(int i = 0; i < w * 2; ++i) f[i] = (f[i] + f_inv[i] * g[i]) % P * i2 % P;
		ntt(f, w << 1, -1);
		memset(f + w, 0, w * 8);
	}
	memset(f + n, 0, (V - n) * 8);
}


void polyder(ll *f, const ll *h, int n) {							// 项数,非次数
	for(int i = 0; i <= n - 1; ++ i) {
		f[i] = (i + 1) * h[i + 1] % P;
	}
	f[n - 1] = 0;
}


void polyint(ll *f, const ll *h, int n) {							// 项数,非次数
	for(int i = n - 1; i >= 1; -- i) {
		f[i] = h[i - 1] * inv[i] % P; 
	}
	f[0] = 0;
}


void polyln(ll *f, const ll *h, int n) {				// h[0] = 1
	static ll a[N], b[N];
	polyinv(a, h, n);
	polyder(b, h, n);
	polymul(a, a, b, n, n);
	polyint(f, a, n);	
}

void polyexp(ll *f, const ll *h, int n) {				// h[0] = 0
	static ll d[N], g[N], f_ln[N];
	int V = 1; while(V < n + n - 1) V <<= 1;
	memcpy(d, h, n * 8), memset(d + n, 0, (V - n) * 8);
	memset(f, 0, V * 8), memset(g, 0, V * 8), memset(f_ln, 0, V * 8);
	f[0] = 1;	//ln(h[0])
	for(int w = 2; w / 2 < n; w <<= 1){
		memcpy(g, d, w * 8);
		for(int i = 0; i < w * 2; ++i) rev[i] = (rev[i >> 1] >> 1) | (i & 1 ? w : 0);
		polyln(f_ln, f, w);
		ntt(f, w << 1, 1), ntt(g, w << 1, 1), ntt(f_ln, w << 1, 1);
		for(int i = 0; i < w * 2; ++i) f[i] = (1 - f_ln[i] + g[i]) % P * f[i] % P;
		ntt(f, w << 1, -1);
		memset(f + w, 0, w * 8);
	}
	memset(f + n, 0, (V - n) * 8);
}

void polypow(ll *f, ll *h, string K, int n) {
	int cur = 0;
	while(cur < n && h[cur] == 0) ++ cur;
	ll k1 = 0, k2 = 0;
	for(auto ch : K) {
		if((k1 * 10 + ch - '0') * cur >= n || cur == n) {
			for(int i = 0; i < n; ++ i) f[i] = 0;
			return;
		}
		k1 = (k1 * 10 + ch - '0') % P;
		k2 = (k2 * 10 + ch - '0') % (P - 1);
	}
	
	ll h_cur = h[cur], icur = qpow(h_cur, P - 2);
	for(int i = cur; i < n; ++ i) {
		f[i - cur] = h[i] * icur % P;
	}
	polyln(f, f, n - cur);
	for(int i = 0; i < n - cur; ++ i) f[i] = f[i] * k1 % P;
	polyexp(f, f, n - cur);
	h_cur = qpow(h_cur, k2);
	for(int i = n - 1; i >= k1 * cur; -- i) f[i] = f[i - k1 * cur] * h_cur % P;
	for(int i = 0; i < k1 * cur; ++ i) f[i] = 0;
}

void Stirling2ndCol(ll *f, int n, int k) { //S2(0...n, k)
	for(int i = 1; i <= n; ++ i) {
		f[i] = ifac[i];
	}
	polypow(f, f, to_string(k), n + 1);
	
	for(int i = 0; i <= n; ++ i) {
		f[i] = f[i] * ifac[k] % P * fac[i] % P;
	}
}

void Stirling2ndRow(ll *f, int n) { // S2(n, 0...n)
	static ll g[N];
	for(int i = 0; i <= n; ++ i) {
		f[i] = (i & 1) ? -ifac[i] : ifac[i];
		g[i] = qpow(i, n) * ifac[i] % P;
	}
	polymul(f, f, g, n + 1, n + 1);
}

void Stirling1stCol(ll *f, int n, int k) { //c(0...n, k)
	for(int i = 2; i <= n; ++ i) {
		f[i] = 0;
	}
	f[0] = 1, f[1] = -1;
	polyln(f, f, n + 1);
	for(int i = 0; i <= n; ++ i) f[i] = -f[i];
	
	polypow(f, f, to_string(k), n + 1);
	for(int i = 0; i <= n; ++ i) {
		f[i] = f[i] * ifac[k] % P * fac[i] % P;
	}
}

void Offset(ll *f, ll *h, int n, int c) {		// f <-- h(x + c) 偏移   n次多项式
	static ll g[N], d[N];
	for(int i = 0; i <= n; ++ i) {
		g[i] = fac[i] * h[i] % P;
		d[i] = (i == 0 ? 1 : d[i - 1] * c % P);
	}
	for(int i = 0; i <= n; ++ i) d[i] = d[i] * ifac[i] % P;
	reverse(d, d + n + 1);
	polymul(f, g, d, n + 1, n + 1);
	for(int i = 0; i <= n; ++ i) {
		f[i] = f[i +  n] * ifac[i] % P;
	}
	memset(f + n + 1, 0, n);
}

void Stirling1stRow(ll *f, int n) { // c(n, 0...n)
	static int stk[30], tp;
	static ll g[N];
	tp = 0;
	while(n) {
		stk[++ tp] = n;
		n >>= 1;
	}
	f[0] = 0, f[1] = 1;
	n = 1;
	while(-- tp) {
		Offset(g, f, n, n);
		polymul(f, f, g, n + 1, n + 1);
		n <<= 1;
		if(stk[tp] == n + 1) {
			f[n + 1] = f[n];
			for(int i = n; i >= 1; -- i) {
				f[i] = (f[i] * n + f[i - 1]) % P;
			}
			++ n;
		}
	}
}

void Partition(ll *f, int n) {
	memset(f, 0, (n + 1) * 8);
	for(int i = 1; i <= n; ++ i) {
		for(int j = i; j <= n; j += i) {
			f[j] = (f[j] + inv[i]) % P;
		}
	}
	polyexp(f, f, n + 1);
}

ll n, k, f[N];

int main() {
	cin.tie(0)->sync_with_stdio(0);
	init();
	
	return 0;
}
posted @ 2024-05-13 20:24  Lu_xZ  阅读(17)  评论(0编辑  收藏  举报