| tags |
|
|---|
Problems in competitive programming, especially the ones involving enumeration some kind, are often solved by reducing the problem to computing something on polynomials and formal power series.
This includes concepts such as polynomial multiplication, interpolation, and more complicated ones, such as polynomial logarithms and exponents. In this article, a brief overview of such operations and common approaches to them is presented.
In this section, we focus more on the definitions and "intuitive" properties of various polynomial operations. The technical details of their implementation and complexities will be covered in later sections.
!!! info "Definition"
Univariate polynomial is an expression of form
The values
Typical example of such field is the field of remainders modulo prime number
For simplicity we will drop the term univariate, as this is the only kind of polynomials we consider in this article. We will also write
!!! info "Definition" The product of two polynomials is defined by expanding it as an arithmetic expression:
$$
A(x) B(x) = \left(\sum\limits_{i=0}^n a_i x^i \right)\left(\sum\limits_{j=0}^m b_j x^j\right) = \sum\limits_{i,j} a_i b_j x^{i+j} = \sum\limits_{k=0}^{n+m} c_k x^k = C(x).
$$
The sequence $c_0, c_1, \dots, c_{n+m}$ of the coefficients of $C(x)$ is called the **convolution** of $a_0, \dots, a_n$ and $b_0, \dots, b_m$.
!!! info "Definition"
The degree of a polynomial
For consistency, degree of $A(x) = 0$ is defined as $\deg A = -\infty$.
In this notion,
Convolutions are the basis of solving many enumerative problems.
!!! Example
You have
Objects of first kind are valued $a_1, \dots, a_n$, and objects of the second kind are valued $b_1, \dots, b_m$.
You pick a single object of the first kind and a single object of the second kind. How many ways are there to get the total value $k$?
??? hint "Solution"
Consider the product
!!! Example
You throw a
??? hint "Solution"
The answer is the number of outcomes having the sum
What is the number of outcomes having the sum $k$? For $n=1$, it may be represented by a polynomial $A(x) = x^1+x^2+\dots+x^6$.
For $n=2$, using the same approach as in the example above, we conclude that it is represented by the polynomial $(x^1+x^2+\dots+x^6)^2$.
That being said, the answer to the problem is the $k$-th coefficient of $(x^1+x^2+\dots+x^6)^n$, divided by $6^n$.
The coefficient near
!!! info "Definition"
A formal power series is an infinite sum
In other words, when we consider e.g. a sum
!!! info "Definition"
The product of formal power series
$$
A(x) B(x) = \left(\sum\limits_{i=0}^\infty a_i x^i \right)\left(\sum\limits_{j=0}^\infty b_j x^j\right) = \sum\limits_{i,j} a_i b_j x^{i+j} = \sum\limits_{k=0}^{\infty} c_k x^k = C(x),
$$
where the coefficients $c_0, c_1, \dots$ are define as finite sums
$$
c_k = \sum\limits_{i=0}^k a_i b_{k-i}.
$$
The sequence $c_0, c_1, \dots$ is also called a **convolution** of $a_0, a_1, \dots$ and $b_0, b_1, \dots$, generalizing the concept to infinite sequences.
Thus, polynomials may be considered formal power series, but with finite number of coefficients.
Formal power series play a crucial role in enumerative combinatorics, where they're studied as generating functions for various sequences. Detailed explanation of generating functions and the intuition behind them will, unfortunately, be out of scope for this article, therefore the curious reader is referenced e.g. here for details about their combinatorial meaning.
However, we will very briefly mention that if
!!! Example
Let
In a similar way, there is an intuitive meaning to some other functions over formal power series.
Similar to integers, it is possible to define long division on polynomials.
!!! info "Definition"
For any polynomials $A$ and $B \neq 0$, one may represent $A$ as
$$
A = D \cdot B + R,~ \deg R < \deg B,
$$
where $R$ is called the **remainder** of $A$ modulo $B$ and $D$ is called the **quotient**.
Denoting
!!! info "Definition"
If
$$
A \equiv B \pmod{C}.
$$
Polynomial long division is useful because of its many important properties:
-
$A$ is a multiple of$B$ if and only if$A \equiv 0 \pmod B$ . -
It implies that
$A \equiv B \pmod C$ if and only if$A-B$ is a multiple of$C$ . -
In particular,
$A \equiv B \pmod{C \cdot D}$ implies$A \equiv B \pmod{C}$ . -
For any linear polynomial
$x-r$ it holds that$A(x) \equiv A(r) \pmod{x-r}$ . -
It implies that
$A$ is a multiple of$x-r$ if and only if$A(r)=0$ . -
For modulo being
$x^k$ , it holds that$A \equiv a_0 + a_1 x + \dots + a_{k-1} x^{k-1} \pmod{x^k}$ .
Note that long division can't be properly defined for formal power series. Instead, for any
Here you can find the basic implementation of polynomial algebra.
It supports all trivial operations and some other useful methods. The main class is poly<T> for polynomials with coefficients of type T.
All arithmetic operation +, -, *, % and / are supported, % and / standing for remainder and quotient in Euclidean division.
There is also the class modular<m> for performing arithmetic operations on remainders modulo a prime number m.
Other useful functions:
-
deriv(): computes the derivative$P'(x)$ of$P(x)$ . -
integr(): computes the indefinite integral$Q(x) = \int P(x)$ of$P(x)$ such that$Q(0)=0$ . -
inv(size_t n): calculate the first$n$ coefficients of$P^{-1}(x)$ in$O(n \log n)$ . -
log(size_t n): calculate the first$n$ coefficients of$\ln P(x)$ in$O(n \log n)$ . -
exp(size_t n): calculate the first$n$ coefficients of$\exp P(x)$ in$O(n \log n)$ . -
pow(size_t k, size_t n): calculate the first$n$ coefficients for$P^{k}(x)$ in$O(n \log nk)$ . -
deg(): returns the degree of$P(x)$ . -
lead(): returns the coefficient of$x^{\deg P(x)}$ . -
resultant(poly<T> a, poly<T> b): computes the resultant of$a$ and$b$ in$O(|a| \cdot |b|)$ . -
bpow(T x, size_t n): computes$x^n$ . -
bpow(T x, size_t n, T m): computes$x^n \pmod{m}$ . -
chirpz(T z, size_t n): computes$P(1), P(z), P(z^2), \dots, P(z^{n-1})$ in$O(n \log n)$ . -
vector<T> eval(vector<T> x): evaluates$P(x_1), \dots, P(x_n)$ in$O(n \log^2 n)$ . -
poly<T> inter(vector<T> x, vector<T> y): interpolates a polynomial by a set of pairs$P(x_i) = y_i$ in$O(n \log^2 n)$ . - And some more, feel free to explore the code!
The very core operation is the multiplication of two polynomials. That is, given the polynomials
You have to compute polynomial
It can be computed in
If
This algorithm was mentioned in Schönhage's article and is inspired by Graeffe's method. It is known that for
Note that
The complexity of this method can be estimated as
The generic process described here is known as Hensel lifting, as it follows from Hensel's lemma. We'll cover it in more detail further below, but for now let's focus on ad hoc solution. "Lifting" part here means that we start with the approximation
Let
Let
From this, one can obtain the final formula, which is
Thus starting with
The algorithm here might seem a bit more complicated than the first one, but it has a very solid and practical reasoning behind it, as well as a great generalization potential if looked from a different perspective, which would be explained further below.
Consider two polynomials
Let
The system of linear equations we're talking about can be written in the following form:
From the looks of it, we can conclude that with the introduction of reversed polynomials
the system may be rewritten as
From this you can unambiguously recover all coefficients of
And from this, in turn, you can recover
Note that the matrix above is a so-called triangular Toeplitz matrix and, as we see here, solving system of linear equations with arbitrary Toeplitz matrix is, in fact, equivalent to polynomial inversion. Moreover, inverse matrix of it would also be triangular Toeplitz matrix and its entries, in terms used above, are the coefficients of
Let's generalize the Sieveking–Kung algorithm. Consider equation
where
where
and
Let
Substituting
Since
The last formula gives us the value of
Thus, knowing how to invert polynomials and how to compute
where
The iterative rule above is known in numerical analysis as Newton's method.
As was mentioned earlier, formally and generically this result is known as Hensel's lemma and it may in fact used in even broader sense when we work with a series of nested rings. In this particular case we worked with a sequence of polynomial remainders modulo
Another example where Hensel's lifting might be helpful are so-called p-adic numbers where we, in fact, work with the sequence of integer remainders modulo
For the function
Thus we can calculate
Turns out, we can get the formula for
Let's learn to calculate
Now we need to calculate
Note though, that you can calculate the logarithms and the exponents correctly only if you can find some initial
To find it, you should calculate the logarithm or the exponent of the constant coefficient of the polynomial.
But the only reasonable way to do it is if
Thus you can use formula above only if
Note that you also can calculate some
For the particular case when you need to evaluate a polynomial in the points
Let's substitute
Which is up to the factor
Note that
Now if you need to evaluate a polynomial in the points
It gives us an
Another observation is that
The coefficient of
Assume you need to calculate
- Compute a segment tree such that in the segment
$[l,r)$ stands the product$P_{l, r}(x) = (x-x_l)(x-x_{l+1})\dots(x-x_{r-1})$ . - Starting with
$l=1$ and$r=n+1$ at the root node. Let$m=\lfloor(l+r)/2\rfloor$ . Let's move down to$[l,m)$ with the polynomial$A(x) \pmod{P_{l,m}(x)}$ . - This will recursively compute
$A(x_l), \dots, A(x_{m-1})$ , now do the same for$[m,r)$ with$A(x) \pmod{P_{m,r}(x)}$ . - Concatenate the results from the first and second recursive call and return them.
The whole procedure will run in
There's a direct formula by Lagrange to interpolate a polynomial, given set of pairs
Computing it directly is a hard thing but turns out, we may compute it in
Consider
But if you consider the derivative
Now consider the recursive algorithm done on same segment tree as in the multi-point evaluation. It starts in the leaves with the value
When we return from the recursion we should merge the results from the left and the right vertices as
In this way when you return back to the root you'll have exactly
Assume you're given polynomials
Let
You want to know if
Well, we already have an article about it. For an arbitrary domain you can write the Euclidean algorithm as easy as:
template<typename T>
T gcd(const T &a, const T &b) {
return b == T(0) ? a : gcd(b, a % b);
}It can be proven that for polynomials
Let's calculate the product
For symmetry we can also multiply it with
The value defined above is called the resultant of the polynomials
-
$\mathcal R(A, B) = (-1)^{nm} \mathcal R(B, A)$ . -
$\mathcal R(A, B)= a_n^m b_m^n$ when$n=0$ or$m=0$ . - If
$b_m=1$ then$\mathcal R(A - CB, B) = \mathcal R(A, B)$ for an arbitrary polynomial$C(x)$ and$n,m \geq 1$ . - From this follows
$\mathcal R(A, B) = b_m^{\deg(A) - \deg(A-CB)}\mathcal R(A - CB, B)$ for arbitrary$A(x)$ ,$B(x)$ ,$C(x)$ .
Miraculously it means that resultant of two polynomials is actually always from the same ring as their coefficients!
Also these properties allow us to calculate the resultant alongside the Euclidean algorithm, which works in
template<typename T>
T resultant(poly<T> a, poly<T> b) {
if(b.is_zero()) {
return 0;
} else if(b.deg() == 0) {
return bpow(b.lead(), a.deg());
} else {
int pw = a.deg();
a %= b;
pw -= a.deg();
base mul = bpow(b.lead(), pw) * base((b.deg() & a.deg() & 1) ? -1 : 1);
base ans = resultant(b, a);
return ans * mul;
}
}There is a way to calculate the GCD and resultants in
The procedure to do so implements a
The specific details of the algorithm are somewhat tedious to explain, however you can find its implementation in the library, as half_gcd function.
After half-GCD is implemented, you can repeatedly apply it to polynomials until you're reduced to the pair of