Encode a sequence as the formal power series
Operations on sequences become algebra on the series, and the coefficient extraction recovers the answer.
The dictionary
| Sequence operation | Series operation |
|---|---|
| (convolution) | |
| (shift right) | |
| (shift left) | |
| (prefix sums) | |
| choose any number of copies of one item | |
| choose at most one copy |
Proof: the dictionary entries
The coefficient of in is by expanding the product. , so is the convolution of with all ones, the prefix sums. by termwise differentiation; . For one item of weight , choosing copies contributes , so unlimited copies give and at most one copy gives ; independent choices multiply because their weights add. counts multisets of size from types, which is by stars and bars.
The essential series
Why they help
Counting with constraints
“How many ways to make using coins , unlimited?”
and the answer is of that. Each factor encodes one coin’s choices; multiplying combines them. This makes coin change a mechanical derivation rather than a DP to be invented.
“…using each coin at most twice?” replace with .
Solving recurrences
For with :
and partial fractions give Binet’s formula: with , , so . In general, a linear recurrence corresponds to a rational generating function, whose denominator is the reversed characteristic polynomial. That correspondence is what Bostan-Mori exploits to compute the -th term in .
Deriving identities
gives Vandermonde’s identity by comparing coefficients. Most binomial identities fall out this way.
Proof: linear recurrences are rational generating functions
If for , then satisfies for , so is a polynomial of degree and . Conversely, if with and , comparing coefficients of gives the recurrence. is the characteristic polynomial with reversed coefficients.
Code: series arithmetic and counting, O(n²)
The versions below are enough for and are what you type in a contest when NTT is not needed; the versions are in Formal Power Series.
const long long MOD = 998244353;
long long pw(long long a, long long e) { long long r = 1; a %= MOD; for (; e; e >>= 1, a = a * a % MOD) if (e & 1) r = r * a % MOD; return r; }
vector<long long> mulTrunc(const vector<long long>& a, const vector<long long>& b, int n) { // first n coefficients of a*b, O(n^2)
vector<long long> c(n, 0);
for (int i = 0; i < sz(a) && i < n; i++) if (a[i]) for (int j = 0; j < sz(b) && i + j < n; j++) c[i + j] = (c[i + j] + a[i] * b[j]) % MOD;
return c;
}
vector<long long> invTrunc(const vector<long long>& a, int n) { // 1/A mod x^n, needs a[0] != 0, O(n^2)
vector<long long> b(n, 0); long long i0 = pw(a[0], MOD - 2); b[0] = i0;
for (int k = 1; k < n; k++) {
long long s = 0;
for (int j = 1; j <= k && j < sz(a); j++) s = (s + a[j] * b[k - j]) % MOD;
b[k] = (MOD - s) % MOD * i0 % MOD;
}
return b;
}
vector<long long> coinWays(const vector<int>& coins, int n) { // [x^k] prod 1/(1 - x^w), k <= n
vector<long long> f(n + 1, 0); f[0] = 1;
for (int w : coins) for (int k = w; k <= n; k++) f[k] = (f[k] + f[k - w]) % MOD; // multiply by 1/(1 - x^w)
return f;
}
vector<long long> connectedFromAll(const vector<long long>& g) { // exponential formula, labelled: g = all (g[0] = 1), returns connected counts
int n = sz(g) - 1; vector<vector<long long>> C(n + 1, vector<long long>(n + 1, 0));
for (int i = 0; i <= n; i++) { C[i][0] = 1; for (int j = 1; j <= i; j++) C[i][j] = (C[i - 1][j - 1] + C[i - 1][j]) % MOD; }
vector<long long> c(n + 1, 0);
for (int m = 1; m <= n; m++) { // root the component containing vertex 1: size k
long long s = 0;
for (int k = 1; k < m; k++) s = (s + C[m - 1][k - 1] * c[k] % MOD * g[m - k]) % MOD;
c[m] = (g[m] - s + MOD) % MOD;
}
return c;
}Proof: the code
Inverse. means , so and , computed in increasing . Coins. Multiplying by means , i.e. , which is the in-place loop. Exponential formula. In a labelled structure on , let be the size of the component containing vertex : choose its other vertices ( ways), a connected structure on them (), and any structure on the rest (). So , which the code solves for ; in EGF terms this is , i.e. . Tests: inverse times series is ; coin counts against brute-force enumeration; connected labelled graphs from ; derangements from against all permutations.
Exponential generating functions
For labelled structures, use . Then the product corresponds to splitting a labelled set into two parts:
| Structure | EGF |
|---|---|
| Any set | |
| Non-empty set | |
| Permutations | |
| Cycles | |
| Derangements | |
| Set partitions (Bell) | |
| Involutions |
The exponential formula — “if is the EGF for connected structures, then is the EGF for all structures” — is remarkably powerful: it turns “count graphs” into “count connected graphs” and back.
Proof: EGF products and the table
, and multiplying by gives : choose which labels go to the first part. Any set: one structure per size, . Permutations: per size, . Cycles: per size, . Permutations are sets of cycles, so (the exponential formula); a derangement is a permutation without fixed points, so its EGF is . Set partitions are sets of non-empty blocks: . Involutions are sets of cycles of length 1 or 2: .
Computing with them
| Operation | Cost |
|---|---|
| Multiply two series to terms | with NTT |
| Inverse | by Newton iteration |
| , | |
| , | |
| Extract of a rational function | (Bostan-Mori) |
| Composition | (or better) |
See Formal Power Series for the implementations.
When to reach for them
- The problem is “count the ways to combine independent choices” multiply the per-choice series.
- The recurrence is linear with constant coefficients rational GF, then Bostan-Mori.
- The objects are labelled and split into independent parts EGF and the exponential formula.
- You need for one huge find the closed form, then extract.
- You need all coefficients up to NTT-based series operations.
Even when you end up writing an ordinary DP, deriving the generating function first often reveals the right state and transition.
Related: Binomial Identities, Knapsack, Linear Recurrences.