---
# System prepended metadata

title: Matrix Exponentials (When DP is Naive)

---

# Matrix Exponentials (When DP is Naive, Use Linear Algebra)

Yep, you read that absolutely right: there exist problems where your clever dynamic programming solution is actually the **naive** solution.

Don’t believe me? Try solving this Fibonacci problem, https://cses.fi/problemset/task/1722 with DP & lmk if you get DP or not.

---

## 0) **Introduction**

So what the hell is matrix exponentiation and why do CP grinders love it?

First step: understand **linear recurrences**.

---

### **Linear Recurrence Refresher**

A **recurrence relation** defines each term of a sequence from previous ones.  
A **linear recurrence** means the next term is a **linear combination** of the last $k$ terms:

$$
\boxed{\displaystyle f(n) = c_1 f(n-1) + c_2 f(n-2) + \cdots + c_k f(n-k).}
$$

- **Linear** = only “coefficient $\times$ old term” plus additions; no squaring, no $\min/\max$.
- The number of past terms ($k$) is the **order**.
- Once you know the first $k$ values, the rest is determined.

**Classic examples**

- Fibonacci: $F(n)=F(n-1)+F(n-2)$.
- Tribonacci: $T(n)=T(n-1)+T(n-2)+T(n-3)$.
- Weighted: $a(n)=2\,a(n-1)-a(n-2)$.

**Non-examples (nonlinear)**

- $f(n)=f(n-1)\cdot f(n-2)$.
- $f(n)=f(n-1)^2$.
- $f(n)=\min\{f(n-1), f(n-2)\}$.

**Big picture:** future $=$ weighted sum of past.

**Note:**
All linear recurrence problems are DP problems -> you can optimize with matrix exponentials. But not all DP problems have a linear recurrence structure.

---

### **Why Matrix Exponentiation?**

Linear recurrences can be written as a matrix multiplying a **state vector**. Instead of computing $f(n)$ step by step (which dies at $n\sim10^{12}$), raise the matrix to a power with **binary exponentiation** and jump straight there.

**Pipeline:**  
recurrence $\to$ state vector $\to$ transition matrix $\to$ fast exponentiation $\to$ result.

**What is Matrix Exponentiation?**  
Take a linear recurrence/DP and rewrite it as
$$
\text{State}(n) = T\,\text{State}(n-1).
$$
Then jump to $n$ by computing $T^{n-1}$ (or $T^n$, see indexing below).

**Why Is It Useful?**  
If you only need the $n$‑th value and $n$ is massive (e.g., $10^{12}$), simulating every step **dies in brute**. Raising $T$ with fast (binary) exponentiation is easy.

---

## 1) **Speed First: Why Binary Exponentiation Is Stupid Fast**

**Goal:** compute $T^p$ without multiplying by $T$ $p$ times.
$$
T^{15} = T^{8}\,T^{4}\,T^{2}\,T^{1}.
$$
(Powers of the **same** matrix commute, so order doesn’t matter.)

**Why it’s fast.**  
You do $O(\log p)$ squarings and at most $O(\log p)$ multiplies to combine them.  

With naive $k\times k$ multiply at $O(k^3)$, total is **$O(k^3\log p)$**.  

Rule of thumb: good for $k\lesssim 50$ per test (constraints matter).

**Minimal code**
```cpp
Matrix mpow(Matrix base, long long p) {
    Matrix ans(base.n, base.mod, /*identity=*/true);
    while (p > 0) {
        if (p & 1LL) ans = ans * base;  // include this power if current bit is 1
        base = base * base;             // square: T -> T^2 -> T^4 -> ...
        p >>= 1;                        // shift exponent right
    }
    return ans;
}
```

---

## 2) **From DP to Linear Algebra (General Recipe)**

Start with a $k$‑term linear recurrence:
$$
\displaystyle f(n) = c_1 f(n-1) + c_2 f(n-2) + \cdots + c_k f(n-k).
$$

**State vector** = just enough info to advance one step:
$$
\text{State}(n) =
\begin{bmatrix}
 f(n)\\
 f(n-1)\\
 \vdots \\
 f(n-k+1)
\end{bmatrix}.
$$

There’s a $k\times k$ **transition (companion) matrix** $T$ such that
$$
T=\begin{bmatrix}
 c_1 & c_2 & \cdots & c_{k-1} & c_k\\
 1   & 0   & \cdots & 0       & 0\\
 0   & 1   & \cdots & 0       & 0\\
 \vdots & \vdots & \ddots & \vdots & \vdots\\
 0 & 0 & \cdots & 1 & 0
\end{bmatrix},
\qquad
\text{State}(n) = T\,\text{State}(n-1).
$$

Unroll:
$$
\text{State}(n) = T^{\,n-1}\,\text{State}(1).
$$

**When is it $T^n$ vs $T^{n-1}$?**
- Base at **step 1** (you know $\text{State}(1)$) $\Rightarrow$ $\text{State}(n)=T^{n-1}\,\text{State}(1)$.
- Base at **step 0** (you know $\text{State}(0)$) $\Rightarrow$ $\text{State}(n)=T^{n}\,\text{State}(0)$.

It’s just indexing. Align it with your base case. **Don’t overthink**—pick the natural base.

---

## 3) **Worked Example: Fibonacci (2×2)**

Recurrence: $F(n)=F(n-1)+F(n-2)$, with $F(0)=0,\;F(1)=1$.

**State and Transition**

$$
\text{State}(n) =
\begin{bmatrix}
F(n) \\
F(n-1)
\end{bmatrix},\qquad
T =
\begin{bmatrix}
1 & 1 \\
1 & 0
\end{bmatrix}.
$$

**Check**

$$
T \begin{bmatrix} F(n-1) \\ F(n-2) \end{bmatrix}
= \begin{bmatrix} F(n-1)+F(n-2) \\ F(n-1) \end{bmatrix}
= \begin{bmatrix} F(n) \\ F(n-1) \end{bmatrix}.
$$

So the recurrence matches.

**Base case and small test**

Pick base
$$
\text{State}(1)=\begin{bmatrix}1\\0\end{bmatrix},
$$
so
$$
\text{State}(n)=T^{n-1}\,\text{State}(1).
$$

Small check:

$$
T^4=\begin{bmatrix}5&3\\3&2\end{bmatrix}
\;\;\Rightarrow\;\; F(5)=5.
$$

### **Fibonacci code (clean)**
```cpp
#include <iostream>
#include <vector>
using namespace std;

using ll = long long;
const long long MOD = 1'000'000'007LL;

struct Matrix {
    int n;
    ll mod;
    vector<vector<ll>> a;
    Matrix(int n, ll mod, bool ident = false) : n(n), mod(mod), a(n, vector<ll>(n, 0)) {
        if (ident) for (int i = 0; i < n; ++i) a[i][i] = 1;
    }
    Matrix operator*(const Matrix& o) const {
        Matrix r(n, mod);
        for (int i = 0; i < n; ++i) {
            for (int k = 0; k < n; ++k) if (a[i][k]) {
                ll aik = a[i][k];
                for (int j = 0; j < n; ++j) {
                    r.a[i][j] = (r.a[i][j] + aik * o.a[k][j]) % mod;
                }
            }
        }
        return r;
    }
};

Matrix mpow(Matrix base, long long p) {
    Matrix ans(base.n, base.mod, true);
    while (p > 0) {
        if (p & 1LL) ans = ans * base;
        base = base * base;
        p >>= 1;
    }
    return ans;
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    long long n;
    cin >> n;

    if (n == 0) { cout << 0 << "\n"; return 0; }
    if (n == 1) { cout << 1 << "\n"; return 0; }

    Matrix T(2, MOD);
    T.a = {{1, 1},
           {1, 0}};

    Matrix P = mpow(T, n - 1);
    // State(n) = P * State(1), State(1) = [1, 0]^T
    // F(n) is the top component -> top-left entry of P
    cout << (P.a[0][0] % MOD) << "\n";
    return 0;
}
```

---

## 4) **Another Example: Graph Paths (Exact Length $k$)**

**Task:** number of length‑$k$ paths from node $1$ to node $n$ in a directed (possibly multi‑edge) graph.

### Naive DP (works, but dies if $k$ is huge)

Let
$$
\begin{aligned}
&dp[\ell][u] := \#\{\text{paths of length }\ell\text{ from }1\text{ to }u\},\\
&dp[0][1]=1,\quad dp[0][u\ne1]=0.
\end{aligned}
$$

Transition:
$$
 dp[\ell+1][u] \;=\; \sum_{v} dp[\ell][v]\cdot \#\text{edges}(v\to u).
$$

### Adjacency Matrix View

Build $A$ with **row = from, col = to**:
$$
A[v][u] := \#\text{edges}(v\to u).
$$

Treat $dp[\ell]$ as a **row vector** $[dp[\ell][1],\dots,dp[\ell][n]]$. Then
$$
 dp[\ell+1] = dp[\ell]\,A,\qquad
 dp[k] = dp[0] \; A^k,\qquad
 dp[0] = e_1^\top = [1,0,\dots,0].
$$

Answer for paths $1\to n$ of length $k$ is
$$
 dp[k][n] = (A^k)[1][n] \text{ in 1-indexed math}
 \;\Longleftrightarrow\;
 (A^k)[0][n-1] \text{ in 0-indexed code}.
$$

### **Graph Paths Code**
```cpp
#include <iostream>
#include <vector>
using namespace std;

using ll = long long;
const ll MOD = 1'000'000'007LL;

struct Matrix {
    int n;
    ll mod;
    vector<vector<ll>> a;
    Matrix(int n, ll mod, bool ident = false) : n(n), mod(mod), a(n, vector<ll>(n, 0)) {
        if (ident) for (int i = 0; i < n; ++i) a[i][i] = 1;
    }
    Matrix operator*(const Matrix& o) const {
        Matrix r(n, mod);
        for (int i = 0; i < n; ++i) {
            for (int k = 0; k < n; ++k) if (a[i][k]) {
                ll aik = a[i][k];
                for (int j = 0; j < n; ++j) {
                    r.a[i][j] = (r.a[i][j] + aik * o.a[k][j]) % mod;
                }
            }
        }
        return r;
    }
};

Matrix mpow(Matrix base, long long p) {
    Matrix ans(base.n, base.mod, true);
    while (p > 0) {
        if (p & 1LL) ans = ans * base;
        base = base * base;
        p >>= 1;
    }
    return ans;
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int n, m; long long k;
    cin >> n >> m >> k;

    Matrix A(n, MOD); // A[from][to]
    for (int i = 0; i < m; ++i) {
        int from, to; // edge from -> to, 1-indexed input
        cin >> from >> to;
        --from; --to;
        A.a[from][to] = (A.a[from][to] + 1) % MOD; // row=from, col=to
    }

    Matrix Ak = mpow(A, k);

    // dp[0] = e1^T picks the first row when multiplying on the left:
    // dp[k][n] = (e1^T * A^k)[n] = (A^k)[0][n-1]
    cout << Ak.a[0][n-1] << "\n";
    return 0;
}
```

**Why multi‑edges become counts:** each parallel edge is a distinct choice in a path, so it adds $+1$ into $A[\text{from}][\text{to}]$.

---

## 5) **How to Define the State & Transition (Formal Derivation)**

### 5.1 General $k$‑term linear recurrence

Given
$$
 f(n) = c_1 f(n-1) + c_2 f(n-2) + \cdots + c_k f(n-k),
$$

define the **state**
$$
S(n)=\begin{bmatrix}
 f(n)\\
 f(n-1)\\
 \vdots \\
 f(n-k+1)
\end{bmatrix}.
$$

Then
$$
S(n) =
\underbrace{\begin{bmatrix}
 c_1 & c_2 & \cdots & c_{k-1} & c_k\\
 1   & 0   & \cdots & 0       & 0\\
 0   & 1   & \cdots & 0       & 0\\
 \vdots & \vdots & \ddots & \vdots & \vdots\\
 0 & 0 & \cdots & 1 & 0
\end{bmatrix}}_{T}
\, S(n-1),\qquad
S(n)=T^{\,n-n_0}S(n_0).
$$

Pick $n_0$ so all entries of $S(n_0)$ are known (e.g., $n_0=k$ if you’re given $f(1\dots k)$).

---

### 5.2 Fibonacci
$$
S(n)=\begin{bmatrix}F(n)\\ F(n-1)\end{bmatrix},\quad
T=\begin{bmatrix}1&1\\ 1&0\end{bmatrix},\quad
S(n)=T\,S(n-1),\quad S(1)=\begin{bmatrix}1\\0\end{bmatrix}.
$$

---

### 5.3 Tribonacci

**Recurrence & Bases**
$$
\mathrm{Tri}(n)=\mathrm{Tri}(n-1)+\mathrm{Tri}(n-2)+\mathrm{Tri}(n-3),\quad
\mathrm{Tri}(0)=0,\;\mathrm{Tri}(1)=0,\;\mathrm{Tri}(2)=1.
$$

**State and Transition**
$$
S(n)=\begin{bmatrix}
 \mathrm{Tri}(n)\\
 \mathrm{Tri}(n-1)\\
 \mathrm{Tri}(n-2)
\end{bmatrix},\qquad
T=\begin{bmatrix}
 1&1&1\\
 1&0&0\\
 0&1&0
\end{bmatrix},\qquad
S(n)=T\,S(n-1).
$$

Take $S(2)=[1,0,0]^\top$, then $S(n)=T^{\,n-2}S(2)$.

**Compute $S(7)$ ($\mathrm{Tri}(7)$)**

Squares:
$$
T^2=\begin{bmatrix}2&2&1\\1&1&1\\1&0&0\end{bmatrix},\quad
T^4=(T^2)^2=\begin{bmatrix}7&6&4\\4&3&2\\2&2&1\end{bmatrix},\quad
T^5=T^4T=\begin{bmatrix}13&11&7\\7&6&4\\4&3&2\end{bmatrix}.
$$

Jump:
$$
S(7)=T^5 S(2)=\text{first column of }T^5=
\begin{bmatrix}13\\7\\4\end{bmatrix}
\Rightarrow \mathrm{Tri}(7)=13.
$$

---

### 5.4 Graph Paths

Define
$$
 dp[\ell][u] = \#\{\text{paths of length }\ell\text{ from }1\text{ to }u\}.
$$

Let the **state at layer $\ell$** be the **row** vector
$$
S(\ell)\equiv dp[\ell] =
\begin{bmatrix}
 dp[\ell][1] & dp[\ell][2] & \cdots & dp[\ell][n]
\end{bmatrix}.
$$

Build $A$ with **row = from, col = to**:
$$
A[v][u]=\#\text{edges}(v\to u).
$$

Then
$$
 S(\ell+1)=S(\ell)\,A,\qquad S(0)=e_1^\top=[1,0,\dots,0],
$$
so
$$
 S(k)=e_1^\top A^k,\quad
 \#\text{paths }1\to n\text{ of length }k \;=\; (A^k)[1][n] \text{ (1‑index)} \;=\; (A^k)[0][n-1] \text{ (0‑index)}.
$$

---

## 6) **Closing Notes (Pattern + Caveats)**

### **Pattern**
1. **Define state.** Package just enough info into a vector so one step forward is linear.
2. **Build transition $T$.** Write the recurrence as a dot product of coefficients with past state.
3. **Choose base index.** Decide if your base is $\text{State}(0)$ or $\text{State}(1)$.
4. **Compute $T^{\text{power}}$** with **binary exponentiation** — $O(k^3\log n)$.
5. **Multiply once by the base vector.** That gives $\text{State}(n)$.

### **How to Spot it From a Statement**
- The next answer is a **weighted sum** of the last $k$ answers (not min/max/product/square).
- **Coefficients are constant** in $n$ (fixed transition).
- Graph path DP that looks like
  $$dp[\ell+1][u] = \sum_v dp[\ell][v] \cdot \text{edges}(v\to u)$$
  is literally matrix multiplication with your $A[v][u]$.
- If $n$ or $k$ goes up to $10^9$, $10^{12}$, $10^{18}$, step‑by‑step DP is impossible — use matrix expo.

### **Where It Shines**
- Fibonacci/Tribonacci/**any** fixed $k$‑term linear recurrence.
- Population/Markov models.
- Graph path counts of exact length $k$.
- Automata DP (counting strings under constraints).

### **Caveats**
- Always mod after add/mul; use 64‑bit intermediates before mod.
- $O(k^3\log n)$ can die when $k$ is large (and squaring densifies matrices).
- Don’t overkill: if $n$ is small, plain DP may be faster/simpler.

**Aside.** For 1‑D recurrences there’s also the **Kitamasa method**, which targets $f(n)$ without building a full $k\times k$ matrix; out of scope here.


**TLDR**  
If the problem screams “linear recurrence + huge $n$,” DP is naive.  
Answer = define state $\to$ build transition $\to$ fast power. Enjoy mogging everyone else.

---

## Recommended Problems
* [USACO Guide - Matrix Exponentiation](https://usaco.guide/plat/matrix-expo)
* [UCLA ICPC Spring Training #4 Contest](https://vjudge.net/contest/628365)