Hello Codeforces!
Combinatorics and counting problems are a staple in competitive programming. Today, I want to discuss a classic yet incredibly powerful concept that frequently appears in $$$O(N)$$$ DP optimizations and math-heavy problems: Derangements.
Whether you are grinding Div. 2 B/C problems or tackling high-level combinatorics on USACO and AtCoder, understanding the intuition behind derangements is a massive advantage. Let's dive in!
1. What is a Derangement?
A derangement is a permutation of the elements of a set such that no element appears in its original position.
If we have an array $$$A = [1, 2, 3, \dots, N]$$$, a derangement is a permutation $$$P$$$ where $$$P[i] \neq i$$$ for all $$$1 \le i \le N$$$.
Classic Examples: * Secret Santa: $$$N$$$ people put their names in a hat and each draws one. A valid state is when nobody draws their own name. * The Hat-Check Problem: $$$N$$$ guests check their hats. The attendant hands them back randomly. What is the probability nobody gets their own hat?
The number of derangements of a set of size $$$N$$$ is denoted as $$$!N$$$ (read as subfactorial $$$N$$$) or $$$D_n$$$.
2. The Core Mathematics & Recurrences
To solve this efficiently, we don't generate permutations. Instead, we rely on recurrence relations.
Recurrence 1: The Intuitive Approach
The most standard recurrence for derangements is:
Proof / Intuition: Imagine $$$N$$$ people. Let person $$$1$$$ take the spot of person $$$i$$$ ($$$2 \le i \le n$$$). There are exactly $$$(n - 1)$$$ choices for $$$i$$$. Now, what does person $$$i$$$ do? 1. Case 1 (They swap): Person $$$i$$$ takes person $$$1$$$'s original spot. The two people have perfectly swapped. The remaining $$$(n - 2)$$$ people must now be deranged among themselves. This gives $$$D_{n-2}$$$ ways. 2. Case 2 (They don't swap): Person $$$i$$$ does not take person $$$1$$$'s spot. This is mathematically identical to saying person $$$i$$$ is forbidden from exactly one specific spot (spot 1), while the other $$$(n-2)$$$ people are forbidden from their own spots. This maps perfectly to a derangement problem of size $$$(n - 1)$$$. This gives $$$D_{n-1}$$$ ways.
Summing these cases and multiplying by the $$$(n - 1)$$$ choices yields our formula!
Recurrence 2: The Linear Step
A tighter recurrence relation exists, which is incredibly useful for space optimization:
The Closed Form (Inclusion-Exclusion)
Using the Principle of Inclusion-Exclusion (PIE), the exact formula is:
(Fun fact: As $$$n \to \infty$$$, the probability of a random permutation being a derangement approaches $$$\frac{1}{e} \approx 36.78\%$$$.)
3. Implementation (C++)
In CP, we usually need to compute $$$D_n \pmod M$$$ (where $$$M = 10^9+7$$$ or $$$998244353$$$).
Approach A: $$$O(N)$$$ Precomputation
Perfect when you need to answer multiple test cases. Space Complexity: $$$O(N)$$$. Time Complexity: $$$O(N)$$$.
#include <iostream>
#include <vector>
using namespace std;
const int MOD = 1e9 + 7;
const int MAXN = 1e6;
vector<long long> D(MAXN + 1);
void precompute() {
D[0] = 1; // Base case: 1 way to derange 0 elements
D[1] = 0; // Base case: 0 ways to derange 1 element
for (int i = 2; i <= MAXN; ++i) {
D[i] = (i - 1) * (D[i - 1] + D[i - 2]) % MOD;
}
}
void solve() {
int n;
cin >> n;
cout << D[n] << "\n";
}
int main() {
ios_base::sync_with_stdio(false);
cin.tie(NULL);
precompute();
int t;
cin >> t;
while (t--) {
solve();
}
return 0;
}
Approach B: $$$O(1)$$$ Space Optimization
If you only need to query $$$D_n$$$ once for a massive $$$N$$$, you can roll the variables just like the Fibonacci sequence. Space Complexity: $$$O(1)$$$.
long long get_derangement(int n) {
if (n == 0) return 1;
if (n == 1) return 0;
long long prev2 = 1; // D[0]
long long prev1 = 0; // D[1]
long long current = 0;
for (int i = 2; i <= n; ++i) {
current = (i - 1) * (prev1 + prev2) % MOD;
prev2 = prev1;
prev1 = current;
}
return current;
}
4. Advanced Variations (For Div 1 / Hard Div 2)
Partial Derangements (Rencontres Numbers): What if a problem asks: "How many permutations exist where exactly $$$K$$$ elements are in their original positions?"
You choose which $$$K$$$ elements stay in place, and then completely derange the remaining $$$N - K$$$ elements:
Massive $$$N$$$ Queries using NTT: If $$$N \ge 10^5$$$ and the modulus allows, you can view the closed-form PIE formula as a convolution of two polynomials: $$$A[i] = \frac{(-1)^i}{i!}$$$ and $$$B[j] = j!$$$. This allows computing the entire array using Number Theoretic Transform (NTT) in $$$O(N \log N)$$$ time, though standard $$$O(N)$$$ is almost always sufficient unless there's a strict time limit on array generation.
5. Practice Problems
- CSES Task 1717 — Christmas Party
- Codeforces 1530D — Secret Santa
- Codeforces 340E — Iahub and Permutations
I hope this guide helps solidify your understanding of derangements! Feel free to ask questions in the comments or point out any other interesting problems that use this concept.
Happy coding!








very interesting blog, i remember seeing something like this on a math comp. Hopefully i get to use this in future
Thanks for reading! I'm happy you found it interesting. Hopefully it'll be useful in a future contest. I'd appreciate an upvote if you found the blog helpful.