定义

普通生成函数 OGF 和指数生成函数 EGF 的形式幂级数定义为:

F(x)=i0aixiF(x)=i0aii!xi\begin{aligned} F(x)&=\sum_{i\ge 0} a_ix^i\\ F(x)&=\sum_{i\ge 0}\frac{a_i}{i!}x^i \end{aligned}

这里形式幂级数的意思是不关心自变量的取值而关心每一项的系数,可以是有穷的,也可以是无穷的。

卷积

对于 OGF 的卷积(分别为 ai,bia_i,b_i 的 OGF)结果为:

F(x)G(x)=(i0aixi)(j0bjxj)=i0j0aibjxi+j=k0(i=0kaibki)xk\begin{aligned} F(x)G(x)&=\left(\sum_{i\ge 0} a_ix^i\right)\left(\sum_{j\ge 0}b_j x^j\right)\\ &=\sum_{i\ge 0}\sum_{j\ge 0} a_ib_jx^{i+j}\\ &=\sum_{k\ge 0} \left(\sum_{i=0}^k a_ib_{k-i}\right)x^k \end{aligned}

即结果为 ck=i=0kaibkic_k=\sum_{i=0}^k a_ib_{k-i}​​ 的 OGF。

对于 EGF 的卷积(分别为 ai,bia_i,b_i 的 EGF)结果为:

F(x)G(x)=(i0aii!xi)(j0bjj!xj)=k0(i=0k(ki)aibki)xkk!\begin{aligned} F(x)G(x)&=\left(\sum_{i\ge 0}\frac{a_i}{i!}x^i\right)\left(\sum_{j\ge 0}\frac{b_j}{j!}x^j\right)\\ &=\sum_{k\ge 0}\left(\sum_{i=0}^k\binom{k}{i}a_ib_{k-i}\right)\frac{x^k}{k!} \end{aligned}

即结果为 ck=i=0k(ki)aibkic_k=\sum_{i=0}^k\binom{k}{i}a_ib_{k-i} 的 EGF。

组合数恒等式

正式开始运用 OGF 前,需要了解一个组合数的恒等式。

由组合意义可知 (nm)=(n1m)+(n1m1)\binom{n}{m}=\binom{n-1}{m}+\binom{n-1}{m-1},不断地展开后面一项可以得到:

(nm)=(n1m)+(n2m1)++(nm10)(nm)=i=0m(ni1mi)\begin{aligned} \binom{n}{m}&=\binom{n-1}{m}+\binom{n-2}{m-1}+\cdots+\binom{n-m-1}{0}\\ \binom{n}{m}&=\sum_{i=0}^m\binom{n-i-1}{m-i} \end{aligned}

一个比较常见的形式是:

(n+kn)=i=0m(n+k1iki)=i=0m(n+i1i)\binom{n+k}{n}=\sum_{i=0}^m\binom{n+k-1-i}{k-i}=\sum_{i=0}^m\binom{n+i-1}{i}

这个恒等式用在求证 1(1x)n+1\frac{1}{(1-x)^{n+1}} 的系数,也可以用广义二项式定理求。

封闭形式

下面给出几个常见数列 OGF 的封闭形式:

  1. 对于 ai=1a_i=1 的 OGF 为:

    F(x)=i0xiF(x)=xF(x)+1F(x)=11x\begin{aligned} F(x)&=\sum_{i\ge 0} x^i\\ F(x)&=xF(x)+1\\ F(x)&=\frac{1}{1-x} \end{aligned}

    注意这里并不关心级数是否收敛,因为我们关心的是系数。仅仅是认为 1G(x)\frac{1}{G(x)} 意义是乘以 G(x)G(x) 结果为 11 的多项式。

  2. 对于 ai=i+1a_i=i+1 的 OGF 为:

    F(x)=i0(i+1)xi=i1ixi1=i0(xi)=(11x)=1(1x)2\begin{aligned} F(x)&=\sum_{i\ge 0} (i+1)x^i\\ &=\sum_{i\ge 1} ix^{i-1}\\ &=\sum_{i\ge 0} (x^i)'\\ &=\left(\frac{1}{1-x}\right)'\\ &=\frac{1}{(1-x)^2} \end{aligned}

  3. 对于 ai=(ni)a_i=\binom{n}{i} 的 OGF 为:

    F(x)=i0(ni)xi=(1+x)n\begin{aligned} F(x)&=\sum_{i\ge 0}\binom{n}{i} x^i\\ &=(1+x)^n \end{aligned}

  4. 对于 ai=(n+in)a_i=\binom{n+i}{n} 的 OGF 为:

    F(x)=i0(n+in)xi=1(1x)n+1\begin{aligned} F(x)&=\sum_{i\ge 0}\binom{n+i}{n}x^i\\ &=\frac{1}{(1-x)^{n+1}} \end{aligned}

    用归纳法证明,当 n=0n=0ai=1a_i=1 成立,当 n>0n>0 时:

    F(x)=11x1(1x)n=(i0xi)(j0(n+j1n1)xj)=i0j0(n+j1n1)xi+j=k0(j=0k(n+j1j))xk=k0(n+kn)xk\begin{aligned} F(x)&=\frac{1}{1-x}\frac{1}{(1-x)^n}\\ &=\left(\sum_{i\ge 0} x^i\right)\left(\sum_{j\ge 0}\binom{n+j-1}{n-1} x^j\right)\\ &=\sum_{i\ge 0}\sum_{j\ge 0}\binom{n+j-1}{n-1}x^{i+j}\\ &=\sum_{k\ge 0}\left(\sum_{j=0}^k\binom{n+j-1}{j}\right) x^k\\ &=\sum_{k\ge 0}\binom{n+k}{n}x^k \end{aligned}

    得证,也就是说 [xi]1(1x)n+1=(n+in)[x^i]\frac{1}{(1-x)^{n+1}}=\binom{n+i}{n}​​。

对于 EGF 常见的没有那么多:

  1. 对于 ai=1a_i=1 的 EGF 为:

    F(x)=i0xii!=exF(x)=\sum_{i\ge 0} \frac{x^i}{i!}=e^x

  2. 对于 ai=[2i]a_i=[2\mid i] 的 EGF 为:

    F(x)=i0x2i(2i)!=ex+ex2F(x)=\sum_{i\ge 0}\frac{x^{2i}}{(2i)!}=\frac{e^x+e^{-x}}{2}

  3. 对于 ai=[2(i+1)]a_i=[2\mid (i+1)] 的 EGF 为:

    F(x)=i0x2i+1(2i+1)!=exex2F(x)=\sum_{i\ge 0}\frac{x^{2i+1}}{(2i+1)!}=\frac{e^x-e^{-x}}{2}

利用这些封闭形式对推式子化简帮助很大。

[BZOJ3028] 食物

认为每种食物都是以『个』为单位,只要总数加起来是 nn 就算一种方案。对于给出的 nn,计算出方案数,并对 1000710007 取模,其中 1n105001\leq n\leq 10^{500},每种食物的限制如下:

  1. 偶数个
  2. 00 个或 11
  3. 00 个,11 个或 22
  4. 奇数个
  5. 44 的倍数个
  6. 00 个,11 个,22 个或 33
  7. 不超过一个
  8. 33 的倍数个

题目链接:P10780

如果能写出所有项的 OGF,由于两个 OGF 的卷积意义为 [xn]F(x)G(x)=i=0naibni[x^n]F(x)G(x)=\sum_{i=0}^n a_ib_{n-i},所以只要求出 [xn]Fi(x)[x^n]\prod F_i(x) 即可。

  1. i0x2i=11x2\sum_{i\ge 0} x^{2i}=\frac{1}{1-x^2}
  2. 1+x1+x
  3. 1+x+x2=1x31x1+x+x^2=\frac{1-x^3}{1-x}
  4. i0x2i+1=x1x2\sum_{i\ge0} x^{2i+1}=\frac{x}{1-x^2}
  5. i0x4i=11x4\sum_{i\ge 0} x^{4i}=\frac{1}{1-x^4}
  6. 1+x+x2+x3=1x41x1+x+x^2+x^3=\frac{1-x^4}{1-x}
  7. 1+x1+x
  8. i0x3i=11x3\sum_{i\ge 0}x^{3i}=\frac{1}{1-x^3}

全都乘一起的结果是 F(x)=x(1x)4F(x)=\frac{x}{(1-x)^4},先前证明过:

F(x)=xi0(i+33)xi=i1(i+23)xiF(x)=x\sum_{i\ge 0}\binom{i+3}{3}x^{i}=\sum_{i\ge 1}\binom{i+2}{3} x^i

因此最终的答案是 [xn]F(x)=(n+23)=n(n+1)(n+2)6[x^n]F(x)=\binom{n+2}{3}=\frac{n(n+1)(n+2)}{6}

1
2
n = int(input()) % 10007
print(n * (n+1) * (n+2) * 1668 % 10007)

[CEOI2004] Sweets

nn 罐糖,每罐有 mim_i 个糖果,每罐中糖果相同,不同罐糖果不同,问吃掉至少 aa 至多 bb 个的方案数,答案对 20042004 取模。

数据范围:1n10,0ab107,0mi1061\le n\le 10,0\le a\le b\le 10^7,0\le m_i\le 10^6

题目链接:P6078

显然地,对于 ii 罐中的 OGF 为 Fi(x)=j=0mixj=1xmi+11xF_i(x)=\sum_{j=0}^{m_i} x^j=\frac{1-x^{m_i+1}}{1-x},于是答案的 OGF 是:

F(x)=i=1nFi(x)=1(1x)ni=1n(1xmi+1)=(i0(n1+ii)xi)(i=1n(1xmi+1))\begin{aligned} F(x)&=\prod_{i=1}^n F_i(x)\\ &=\frac{1}{(1-x)^n}\prod_{i=1}^n(1-x^{m_i+1})\\ &=\left(\sum_{i\ge 0}\binom{n-1+i}{i}x^i\right)\left(\prod_{i=1}^n(1-x^{m_i+1})\right) \end{aligned}

注意到 nn 的范围是 1010,所以直接暴力求解右边的 OGF G(x)G(x),复杂度为 O(2n)O(2^n);然后枚举 kk[xk]G(x)[x^k]G(x),这样它贡献的答案会是:

[xk]G(x)(i=akbk(n1+ii))=[xk]G(x)((n+bkn)(n+ak1n))[x^k]G(x)\left(\sum_{i=a-k}^{b-k}\binom{n-1+i}{i}\right)=[x^k]G(x)\left(\binom{n+b-k}{n}-\binom{n+a-k-1}{n}\right)

还有一个问题,模数非质数,怎么求组合数?注意到我们需要求的形式是 (n+xn)\binom{n+x}{n},不妨展开看一下:

(n+xn)=(n+x)!n!x!=(n+x)nn!\binom{n+x}{n}=\frac{(n+x)!}{n!x!}=\frac{(n+x)^{\underline{n}}}{n!}

上面的式子可以 O(n)O(n) 算出来,设 A=(n+x)nA=(n+x)^{\underline{n}},对 2004n!2004n! 做带余除法,有 A=2004n!k1+rA=2004n!k_1+r,注意到一定有 n!An!\mid A,因此 n!rn!\mid r,并且:

Ar(mod2004n!)An!=2004k1+rn!rn!(n+xn)(mod2004)\begin{aligned} {A}&\equiv r\pmod{2004n!}\\ \frac{A}{n!}&=2004k_1+\frac{r}{n!}\\ \frac{r}{n!}&\equiv \binom{n+x}{n}\pmod {2004} \end{aligned}

由于枚举 kk 时最多有 2n2^n 个非零系数,对它们算组合数有一个 O(n)O(n) 的复杂度,但是相较于 O(b)O(b) 的枚举还是可以忽略的,所以复杂度是 O(2n+b)O(2^n+b)​。

这里先用 map 算,最后将暴力算出来的多项式存在 int16_t F[N] 里,为了加速后面的枚举对 F 的访问速度。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#include <bits/stdc++.h>
using namespace std;

#define int long long
const int N = 1e7+10, P = 2004;

int n, a, b, facn = 1;
int16_t F[N];

int C(int x) {
int mod = P * facn, r = 1;
for (int i = 0; i < n; i++) r = r * (n + x - i) % mod;
return r / facn;
}

void mul(map<int, int>& f, int m) {
// f *= 1 - x^{m+1}
map<int, int> f0 = f;
for (auto& [p, a]: f0) f[p+m+1] = (f[p+m+1] - a) % P;
}

int solve(int k) {
if (F[k] == 0) return 0;
if (k >= a) return F[k] * C(b-k) % P;
return F[k] * (C(b-k) - C(a-k-1)) % P;
}

signed main() {
map<int, int> f = {{0, 1}};
cin >> n >> a >> b;
for (int i = 1, m; i <= n; i++) cin >> m, mul(f, m), facn = facn * i;
for (const auto& [p, a]: f) F[p] = (a + P) % P;
int ans = 0;
for (int k = 0; k <= b; k++) ans = (ans + solve(k)) % P;
if (ans < 0) ans += P;
cout << ans << endl;
return 0;
}

[国家集训队] 整数的 lqp 拆分

f0=0,f1=1,fn=fn1+fn2f_0=0,f_1=1,f_n=f_{n-1}+f_{n-2},给定 nn,求整数 nn 的所有拆分权值之和,定义拆分 iai=n\sum_{i} a_i=n 的权值为 fai\prod f_{a_{i}},其中 1n10100001\le n\le 10^{10000},答案对 109+710^9+7 取模。

题目链接:P4451

定义 gng_nnn 的答案,枚举 gnig_{n-i} 的答案乘 fif_i 就是当前答案。

gn=i=0ngnifig_n=\sum_{i=0}^n g_{n-i}f_i

当然,这里需要令 g0=1g_0=1,满足当新开一个数字时权值为 fnf_n

fi,gif_i,g_i 的 OGF 分别为 F(x),G(x)F(x),G(x),有:

G(x)=F(x)G(x)+1G(x)=F(x)G(x)+1

原因是 [x0]F(x)=0[x^0]F(x)=0,因此 [x0]F(x)G(x)=0[x^0]F(x)G(x)=0,所以补上 [x0]G(x)=g0=1[x^0]G(x)=g_0=1

接下来推导 F(x)F(x) 的封闭形式:

F(x)=i0fixixF(x)=i1fi1xix2F(x)=i2fi2xi(1xx2)F(x)=f0x0+(f1f0)x1F(x)=x1xx2\begin{aligned} F(x)&=\sum_{i\ge 0} f_ix^i\\ xF(x)&=\sum_{i\ge 1} f_{i-1}x^i\\ x^2F(x)&=\sum_{i\ge 2} f_{i-2} x^i\\ (1-x-x^2)F(x)&=f_0x^0+(f_1-f_0)x^1\\ F(x)&=\frac{x}{1-x-x^2} \end{aligned}

代回原式子可以得到:

G(x)=11F(x)=1xx212xx2=1+x12xx2\begin{aligned} G(x)&=\frac{1}{1-F(x)}\\ &=\frac{1-x-x^2}{1-2x-x^2}\\ &=1+\frac{x}{1-2x-x^2} \end{aligned}

所以现在就是要求 [xn]x12xx2[x^n]\frac{x}{1-2x-x^2},注意到这也是一个类似斐波那契的递推数列的 OGF,它应该满足 g0=0,g1=1,gn=2gn1+gn2g_0=0,g_1=1,g_n=2g_{n-1}+g_{n-2},所以直接写一个矩阵快速幂就好了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
P = int(1e9+7)
n = int(input()) - 1
res = [[1, 0], [0, 1]]
a = [[2, 1], [1, 0]]

def mul(A, B):
res = [[0, 0], [0, 0]]
for k in range(2):
for i in range(2):
for j in range(2):
res[i][j] = (res[i][j] + A[i][k] * B[k][j]) % P
return res

while n:
if n & 1: res = mul(res, a)
a = mul(a, a)
n >>= 1

print(res[0][0])

[集训队作业2013] 城市规划

nn 个点简单有标号无项连通图数量,答案对 1004535809(479×221+1)1004535809(479\times 2^{21}+1) 取模,其中 1n1300001\le n\le 130000

题目链接:P4841

gng_n 表示 nn 个点的无向图数量,那么 gn=2(n2)g_n=2^{\binom{n}{2}},可以枚举 11 的连通块大小为 ii,有:

gn=i=1n(n1i1)fignig_n=\sum_{i=1}^n\binom{n-1}{i-1}f_ig_{n-i}

展开,并且两边除以 (n1)!(n-1)! 有:

2(n2)(n1)!=i=1nfx(i1)!2(nx2)(nx)!\frac{2^{\binom{n}{2}}}{(n-1)!}=\sum_{i=1}^n\frac{f_x}{(i-1)!} \frac{2^{\binom{n-x}{2}}}{(n-x)!}

定义 fi,gi=2(i2)f_i,g_i=2^{\binom{i}{2}} 的 EGF 为 F(x),G(x)F(x),G(x),那么有:

[xn1]G(x)=i=1n[xi1]F(x)[xni]G(x)[x^{n-1}]G'(x)=\sum_{i=1}^n [x^{i-1}] F'(x)[x^{n-i}]G(x)

即:

G(x)=F(x)G(x)G'(x)=F'(x)G(x)

所以要求的 F(x)F(x) 为:

F(x)=G(x)G(x)dx=lnG(x)+CF(x)=\int \frac{G'(x)}{G(x)}\mathrm{d}x=\ln G(x)+C

由于 n1n\ge 1,所以常数 CC 的取值无所谓。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include <bits/stdc++.h>
using namespace std;

#define int long long
constexpr int N = 310000, P = 1004535809;
int fact[N], infact[N];

constexpr int qmi(int a, int k) {
int res = 1;
while (k) {
if (k & 1) res = res * a % P;
a = a * a % P;
k >>= 1;
}
return res;
}

struct Poly { /* 省略 */ } G;

signed main() {
int n;
cin >> n;
fact[0] = infact[0] = 1;
for (int i = 1; i <= n; i++) fact[i] = (fact[i-1] * i) % P;
infact[n] = qmi(fact[n], P-2);
for (int i = n-1; i; i--) infact[i] = (infact[i+1] * (i+1)) % P;

G.resize(n);
for (int i = 0; i <= n; i++) G[i] = qmi(2, i * (i-1) / 2) * infact[i] % P;
cout << G.ln(0)[n] * fact[n] % P << endl;
return 0;
}

付公主的背包

nn 件商品,每件体积 viv_i,都有无限件,给定 mm 问对于 s=1ms=1\sim m 恰好装 ss 体积的方案数,答案对 998244353998244353 取模。其中 1n,m105,1vim1\le n,m\le 10^5,1\le v_i\le m

题目链接:P4389

对于每个物品的 OGF 显然为 Fi(x)=j0xvij=11xviF_i(x)=\sum_{j\ge 0} x^{v_ij}=\frac{1}{1-x^{v_i}},答案的 OGF 是 F(x)=i=1nFi(x)F(x)=\prod_{i=1}^n F_i(x),但显然不能进行 nnO(nlogn)O(n\log n)​ 的多项式乘法。

前置知识,ln(1+x)\ln(1+x) 的泰勒展开:

ln(1+x)=i=1+(1)i+1ixiln(1x)=i=1+1ixi\begin{aligned} \ln(1+x)&=\sum_{i=1}^{+\infin}\frac{(-1)^{i+1}}{i} x^i\\ \ln(1-x)&=\sum_{i=1}^{+\infin}-\frac{1}{i}x^i \end{aligned}

因此对其进行恒等变换:

F(x)=exp(lnF(x))=exp(i=1nlnFi(x))=exp(i=1nln11xvi)=exp(i=1nj1xvijj)\begin{aligned} F(x)&=\exp(\ln F(x))\\ &=\exp\left(\sum_{i=1}^n \ln F_i(x)\right)\\ &=\exp\left(\sum_{i=1}^n \ln \frac{1}{1-x^{v_i}}\right)\\ &=\exp\left(\sum_{i=1}^n \sum_{j\ge1} \frac{x^{v_ij}}{j}\right)\\ \end{aligned}

因为最后要求的是 [xs]F(x)[x^s]F(x) 其中 s=1ms=1\sim m,所以对于内部只需要枚举每个 viv_i 的值进行贡献,复杂度是 O(mi)=O(mlogm)O(\sum \frac{m}{i})=O(m\log m),然后进行 exp\exp 复杂度也应当是 O(mlogm)O(m\log m)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include <bits/stdc++.h>
using namespace std;

#define int long long
const int P = 998244353;

constexpr int qmi(int a, int k) {
int res = 1;
while (k) {
if (k & 1) res = res * a % P;
a = a * a % P;
k >>= 1;
}
return res;
}

struct Poly { /* 省略 */ };

const int N = 100010;
int n, m, cnt[N];

signed main() {
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
cin >> n >> m;
Poly f;
f.resize(m);
for (int i = 1, v; i <= n; i++) cin >> v, cnt[v]++;
for (int v = 1; v <= m; v++) {
for (int j = v; j <= m; j += v) {
f[j] = (f[j] + cnt[v] * qmi(j / v, P-2)) % P;
}
}
f = f.exp();
for (int i = 1; i <= m; i++) cout << f[i] << '\n';
return 0;
}

[CF438E] The Child and Binary Tree

给定长度为 nn 的互异正整数序列 cic_i,二叉树有点权,一个树的权值为所有点权之和,所有点权都等于某个 cic_i,对于 1sm1\le s\le m,统计权值为 ss 的二叉树个数,答案对 998244353998244353 取模,其中 1n,m,ci1051\le n,m,c_i\le 10^5​。

题目链接:CF438E

计数 DP,设 fnf_n 表示 nn 的树的个数,那么枚举根的权值为 ii,有:

fn={1n=0i=1n[iC]j=0nifjfnijf_n=\begin{cases} 1\quad n=0\\ \sum_{i=1}^n [i\in C] \sum_{j=0}^{n-i} f_jf_{n-i-j} \end{cases}

gi=[iC]g_i=[i\in C],那么可以看出这是一个卷积的形式,设生成函数分别为 F(x),G(x)F(x),G(x),那么有:

F(x)=G(x)F2(x)+1F(x)=G(x)F^2(x)+1

这里 +1+1 是因为 G(x)G(x) 常数项为 00,这个二元一次方程可以解出:

F(x)=1±14G(x)2G(x)=2114G(x)F(x)=\frac{1\pm \sqrt{1-4G(x)}}{2G(x)}=\frac{2}{1\mp \sqrt{1-4G(x)}}

由于 G(0)=0G(0)=0,所以只能取 ++,因为如果取 - 这个式子无法在 00 处泰勒展开,也就不能写成 cixi\sum c_ix^i 的形式,那么 F(x)=21+14G(x)F(x)=\frac{2}{1+\sqrt{1-4G(x)}}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#include <bits/stdc++.h>
using namespace std;

#define int long long
const int P = 998244353;

constexpr int qmi(int a, int k) {
int res = 1;
while (k) {
if (k & 1) res = res * a % P;
a = a * a % P;
k >>= 1;
}
return res;
}

struct Poly { /* 省略 */ };

const int N = 100010;
int c[N];
Poly G;

signed main() {
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int n, m;
cin >> n >> m;

G.resize(m);
for (int i = 1; i <= n; i++) {
cin >> c[i];
if (c[i] <= m) G[c[i]] = 1;
}

for (int i = 0; i <= m; i++) G[i] = G[i] * (P - 4) % P;
G[0]++;
G = G.sqrt();
G[0]++;
G = G.inv();
for (int i = 0; i <= m; i++) G[i] = G[i] * 2 % P;
for (int i = 1; i <= m; i++) cout << G[i] << '\n';

return 0;
}