Appearance
Combinatorics Basics
AL-10 | The counting foundations: product/sum rules, permutations and combinations, binomial coefficients with factorial / inverse-factorial precompute, and Stars and Bars. Appears in nearly every Codeforces Div 2 C-D tagged combinatorics or math.
Difficulty: [Intermediate]Prerequisites: Math Fundamentals, Number TheoryCF Rating Range: 1400-1900
Scope. This file is the foundations chapter. Catalan numbers and the inclusion-exclusion principle have their own dedicated files — 05-catalan-numbers.md and 06-inclusion-exclusion.md. Pattern boxes below cross-link to them where they appear naturally; do not expect the deep treatment here.
Quick Revisit
- USE WHEN: counting arrangements, selections, or distributions modulo a prime
- INVARIANT: C(n,k) = C(n-1,k-1) + C(n-1,k) (Pascal's recurrence)
- TIME: O(n) precompute factorials, O(1) per C(n,k) query | SPACE: O(n)
- CLASSIC BUG: forgetting inverse factorials -- C(n,k) = fact[n] * inv_fact[k] * inv_fact[n-k] mod p
- PRACTICE: see practice problems in this chapter
Contents
- Intuition
- C++ STL Reference
- Implementation (Contest-Ready)
- Operations Reference
- Problem Patterns & Tricks
- Contest Cheat Sheet
- Gotchas & Debugging
- Self-Test
- Practice Problems
- Further Reading
Intuition
Count the number of paths in a 4x3 grid from the top-left corner to the bottom-right corner, moving only right or down.
The grid has 4 columns and 3 rows, so you must take 3 right-moves and 2 down-moves (for a total of 5 moves).
Naive approach: enumerate every possible path by hand.
Start (0,0) --> Goal (3,2)
Path 1: R R R D D
Path 2: R R D R D
Path 3: R R D D R
Path 4: R D R R D
Path 5: R D R D R
Path 6: R D D R R
Path 7: D R R R D
Path 8: D R R D R
Path 9: D R D R R
Path 10: D D R R REven at this tiny size you have to carefully list all 10 and hope you missed none. For a 20x15 grid the number of paths is astronomical. Enumeration does not scale.
Every path from
Think of it like ordering from a fixed-length menu. You have 5 slots in your order (the 5 moves). You must fill exactly 3 of them with "R" and 2 with "D". The number of ways to choose which 3 slots get "R" is
This works because:
- Every path has the same length:
moves total. - The path is completely determined by which moves are R (the rest are D).
- No two different choices of positions produce the same path.
The insight breaks when the grid has blocked cells, weighted edges, or diagonal moves -- then you need DP or inclusion-exclusion instead of a single binomial coefficient.
Visual Walkthrough — verifying Pascal's recurrence on the grid
The grid-filling DP below is doing exactly Pascal's triangle in disguise: each cell
Fill each cell with the number of distinct paths from
Step 1. The top row and left column each have exactly one path (all R or all D respectively). Seed them with 1.
col 0 1 2 3
row
0 [ 1 1 1 1 ]
1 [ 1 . . . ]
2 [ 1 . . . ]Step 2. Cell
col 0 1 2 3
row
0 [ 1 1 1 1 ]
1 [ 1 *2* . . ]
2 [ 1 . . . ]Step 3. Fill the rest of row 1 and cell
col 0 1 2 3
row
0 [ 1 1 1 1 ]
1 [ 1 2 3 4 ]
2 [ 1 *3* . . ]Cell
Step 4. Complete row 2.
col 0 1 2 3
row
0 [ 1 1 1 1 ]
1 [ 1 2 3 4 ]
2 [ 1 3 6 *10*]Cell
The answer is 10. This matches
Operation count: we computed one addition per interior cell, so
The Invariant
+----------------------------------------------------------+
| INVARIANT: C(n,k) = C(n-1,k-1) + C(n-1,k). Every entry |
| is built from exactly two entries in the previous row. |
+----------------------------------------------------------+Invariant.
, and each cell in Pascal's triangle counts the number of paths from the apex to that position.
Connection to the walkthrough: the grid-filling recurrence
Row
n=0: 1
n=1: 1 1
n=2: 1 2 1
n=3: 1 3 3 1
n=4: 1 4 6 4 1
n=5: 1 5 10 10 5 1
Entry at position k in row n = C(n, k)
Recurrence shown:
C(4,2) + C(4,3) = C(5,3)
6 + 4 = 10Each row sums to
PASCAL'S TRIANGLE -- symmetry and row sums:
Row 0: 1 sum = 1 = 2^0
Row 1: 1 1 sum = 2 = 2^1
Row 2: 1 2 1 sum = 4 = 2^2
Row 3: 1 3 3 1 sum = 8 = 2^3
Row 4: 1 4 6 4 1 sum = 16 = 2^4
Row 5: 1 5 10 10 5 1 sum = 32 = 2^5
Row 6: 1 6 15 20 15 6 1 sum = 64 = 2^6
Symmetry: C(n, k) = C(n, n-k) (read each row forward or backward)
Hockey stick: sum of a diagonal = entry below-right of the last term
C(2,0) + C(3,0) + C(4,0) + C(5,0) = C(6,1)
1 + 1 + 1 + 1 = 4 ???
Actually: C(0,0)+C(1,0)+C(2,0)+C(3,0)+C(4,0) = C(5,1) = 5The value 10 at row 5, position 3 is the same 10 we computed in the grid:
What the Code Won't Teach You
The code gives you C(n, k) in
MODELING DECISION TREE:
"How many ways to..."
│
├─ choose k items from n? ──────────-> C(n, k)
│
├─ distribute n identical ┌─ each bin >= 0? -> C(n+k-1, k-1)
│ items into k bins? ──────────────┤
│ └─ each bin >= 1? -> C(n-1, k-1)
│
├─ arrange items with multinomial:
│ repeated types? ──────────────-> n! / (k₁! * k₂! * ... * kₘ!)
│
├─ avoid certain positions? ───────-> PIE or derangements
│
└─ count balanced structures? ─────-> CatalanThe code also hides the why behind inverse factorials. The backward propagation trick inv_fac[i] = inv_fac[i+1] * (i+1) % MOD works because
When to Reach for This
Trigger phrases in problem statements:
- "choose
from " or "select a subset of size " --> . - "count arrangements", "number of distinct sequences" with fixed symbol counts --> multinomial or binomial.
- "answer modulo
" with tag combinatorics--> precompute factorials and inverse factorials forbinomial queries. - "grid paths", "lattice paths" (right/down only) -->
. - "distribute identical objects into bins" --> Stars and Bars.
Counter-examples (looks like combinatorics but is not):
- "shortest path in weighted grid" --> Dijkstra / BFS, not counting.
- "count subsequences of arbitrary length" --> usually DP, not a closed-form binomial.
- "permutations with complex forbidden patterns" --> may need PIE or DP, not a single
. - Constraints with
and no prime modulus --> Lucas' theorem or a completely different approach; naive factorial precomputation is impossible.
C++ STL Reference
| Function / Class | Header | What it does | Time Complexity |
|---|---|---|---|
__int128 | built-in | 128-bit integer for intermediate products that overflow long long. | -- |
gcd(a, b) | <numeric> | Greatest common divisor (C++17). Needed for simplifying fractions in combinatorial formulas. | |
bitset<N> | <bitset> | Compact bitmask. Use for PIE subset enumeration when | |
__builtin_popcountll(x) | built-in | Count set bits in unsigned long long. Used to determine PIE sign from subset parity. | |
vector<vector<ll>> | <vector> | 2D array for Pascal's triangle DP when | -- |
accumulate(b, e, init) | <numeric> | Sum a range. Useful for summing PIE terms. |
Implementation (Contest-Ready)
Version 1: nCk mod p -- Minimal Contest Template
cpp
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int MOD = 1e9 + 7;
const int MAXN = 2e6 + 5;
ll fac[MAXN], inv_fac[MAXN];
ll power(ll a, ll b, ll mod) {
ll res = 1;
a %= mod;
for (; b > 0; b >>= 1) {
if (b & 1) res = res * a % mod;
a = a * a % mod;
}
return res;
}
void precompute() {
fac[0] = 1;
for (int i = 1; i < MAXN; i++)
fac[i] = fac[i - 1] * i % MOD;
inv_fac[MAXN - 1] = power(fac[MAXN - 1], MOD - 2, MOD);
for (int i = MAXN - 2; i >= 0; i--)
inv_fac[i] = inv_fac[i + 1] * (i + 1) % MOD;
}
ll C(int n, int k) {
if (k < 0 || k > n) return 0;
return fac[n] % MOD * inv_fac[k] % MOD * inv_fac[n - k] % MOD;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
precompute();
int n, k;
cin >> n >> k;
cout << C(n, k) << "\n";
}Version 2: Explained with Comments + Inclusion-Exclusion Template
cpp
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int MOD = 1e9 + 7;
const int MAXN = 2e6 + 5;
ll fac[MAXN], inv_fac[MAXN];
// Binary exponentiation: computes a^b mod m.
// Used once to bootstrap inverse factorials.
ll power(ll a, ll b, ll mod) {
ll res = 1;
a %= mod;
for (; b > 0; b >>= 1) {
if (b & 1) res = res * a % mod;
a = a * a % mod;
}
return res;
}
// Precompute factorial and inverse factorial arrays.
// Key insight: only ONE modular inverse (via Fermat) is needed.
// inv_fac[i] = inv_fac[i+1] * (i+1) because (i!)^{-1} = ((i+1)!)^{-1} * (i+1).
void precompute() {
fac[0] = 1;
for (int i = 1; i < MAXN; i++)
fac[i] = fac[i - 1] * i % MOD;
// Single expensive call: inverse of (MAXN-1)!
inv_fac[MAXN - 1] = power(fac[MAXN - 1], MOD - 2, MOD);
// Propagate backwards: inv_fac[i] = inv_fac[i+1] * (i+1) mod p
for (int i = MAXN - 2; i >= 0; i--)
inv_fac[i] = inv_fac[i + 1] * (i + 1) % MOD;
}
// C(n, k) mod p in O(1). Returns 0 for invalid inputs.
ll C(int n, int k) {
if (k < 0 || k > n) return 0;
return fac[n] % MOD * inv_fac[k] % MOD * inv_fac[n - k] % MOD;
}
// Catalan number: C_n = C(2n, n) / (n + 1) = C(2n, n) - C(2n, n+1)
ll catalan(int n) {
return (C(2 * n, n) - C(2 * n, n + 1) + MOD) % MOD;
}
// --- Inclusion-Exclusion over bitmask ---
// Given m "bad" properties, computes the count of elements satisfying NONE.
// f(mask) returns the count of elements satisfying ALL properties in mask.
// Example: derangements, surjections, coprime counting.
//
// Result = sum over all subsets S of (-1)^|S| * f(S)
// = f({}) - sum|S|=1 f(S) + sum|S|=2 f(S) - ...
//
// Requires m <= 20 (2^m subsets).
template<typename F>
ll inclusion_exclusion(int m, F&& f) {
ll ans = 0;
for (int mask = 0; mask < (1 << m); mask++) {
ll val = f(mask);
// __builtin_popcount gives |S|; odd |S| -> subtract
if (__builtin_popcount(mask) % 2 == 0)
ans = (ans + val) % MOD;
else
ans = (ans - val + MOD) % MOD;
}
return ans;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
precompute();
// Example: count derangements of n elements using PIE.
// A derangement is a permutation with no fixed point.
// Let A_i = {permutations where position i is fixed}.
// |A_i1 ^ A_i2 ^ ... ^ A_ik| = (n - k)! (fix k positions, permute rest).
// There are C(n, k) ways to choose which k are fixed.
// D(n) = sum_{k=0}^{n} (-1)^k * C(n,k) * (n-k)!
int n;
cin >> n;
// PIE approach (direct formula):
ll derangements = 0;
for (int k = 0; k <= n; k++) {
ll term = C(n, k) * fac[n - k] % MOD;
if (k % 2 == 0)
derangements = (derangements + term) % MOD;
else
derangements = (derangements - term + MOD) % MOD;
}
cout << derangements << "\n";
// Alternative: using the inclusion_exclusion template with m small properties.
// Suppose m = number of prime divisors of some N, count integers in [1..N]
// coprime to N.
// This is Euler's totient, but demonstrates the PIE template.
int N = 30;
vector<int> primes = {2, 3, 5}; // prime factors of 30
int m = primes.size();
ll coprime_count = inclusion_exclusion(m, [&](int mask) -> ll {
// Product of primes in the mask
ll prod = 1;
for (int i = 0; i < m; i++)
if (mask >> i & 1)
prod *= primes[i];
return N / prod; // count of multiples of prod in [1..N]
});
// coprime_count = 30 - 15 - 10 - 6 + 5 + 3 + 2 - 1 = 8 = phi(30)
cout << coprime_count << "\n";
}Bonus: Pascal's Triangle DP (when , no modular inverse needed)
cpp
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int MOD = 1e9 + 7;
// Build Pascal's triangle up to row n.
// C[i][j] = C(i, j) mod MOD.
vector<vector<ll>> pascal(int n) {
vector<vector<ll>> C(n + 1, vector<ll>(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;
}
return C;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
auto C = pascal(5000);
cout << C[5000][2500] << "\n";
}Operations Reference
| Operation | Time | Space | Notes |
|---|---|---|---|
| Precompute factorials + inverse factorials | One-time setup. The power call. | ||
| -- | After precomputation. | ||
| Multiply | |||
| Pascal's triangle (build up to row | No modular inverse needed. Limited to | ||
| Catalan number | -- | Just a binomial coefficient. | |
| Inclusion-exclusion over | |||
| Stars and Bars | -- | Single binomial coefficient query. | |
| Derangements (PIE formula) |
Problem Patterns & Tricks
Pattern 1: Direct Counting with
The problem gives a set of size
Approach: Identify what is being chosen. Translate constraints into a binomial coefficient. Precompute factorials.
answer = C(n, k) or product of several C(n_i, k_i)Problems: CF 1462D - Add to Neighbour and Remove, CF 1312C - Adding Powers of Two
Pattern 2: Stars and Bars (Distributing Identical Objects)
"Distribute
Approach: Direct Stars and Bars:
STARS AND BARS -- placing dividers among stars:
Distribute n=5 identical stars into k=3 bins:
Stars: * * * * * Dividers: | | (k-1 = 2 dividers)
Arrangement: * * | * | * * -> bins: [2, 1, 2]
* * * | | * * -> bins: [3, 0, 2]
| * * * * | * -> bins: [0, 4, 1]
Total arrangements = C(n+k-1, k-1) = C(7, 2) = 21
For "each bin >= 1": pre-place 1 star per bin (costs k stars),
then distribute remaining n-k = 2 stars freely:
C(n-1, k-1) = C(4, 2) = 6PIE VENN DIAGRAM -- 3 properties:
┌─────────────────────────────────────┐
│ U (universe) │
│ ┌───────────┐ │
│ │ A \ │
│ │ ┌────┐ \ │
│ │ │A∩B │ ┌──┐ │
│ │ │ │ │A∩│B│ │
│ │ └──┬─┘ │B∩│ │ │
│ │ │A∩B │C │ │ │
│ └──────┤∩C ├──┘ │ │
│ └────┘ C │ │
│ └────┘ │
└─────────────────────────────────────┘
|A ∪ B ∪ C| = |A| + |B| + |C|
- |A∩B| - |A∩C| - |B∩C|
+ |A∩B∩C|
"None of A,B,C" = |U| - |A∪B∪C|// Non-negative: C(n + k - 1, k - 1)
// Positive: C(n - 1, k - 1)Problems: CF 1288C - Two Arrays, CF 1081C - Colorful Bricks
Pattern 3: Inclusion-Exclusion on Bitmask
"Count elements satisfying NONE of
Approach: For each subset
cpp
ll ans = 0;
for (int mask = 0; mask < (1 << m); mask++) {
ll val = count_satisfying_all(mask);
if (__builtin_popcount(mask) % 2 == 0)
ans = (ans + val) % MOD;
else
ans = (ans - val + MOD) % MOD;
}Problems: CF 1559D2 - Mocha and Diana (Hard), CF 451E - Devu and Flowers
Pattern 4: Derangements
"Count permutations with no fixed points" or similar constraint on forbidden positions.
Approach:
Problems: CF 888D - Almost Identity Permutations
Pattern 5: Catalan Number Variants
Problem counts one of: valid bracket sequences of length
Approach: Recognize the Catalan structure.
cpp
ll catalan(int n) {
return (C(2*n, n) - C(2*n, n+1) + MOD) % MOD;
}Problems: CF 1264D - Beautiful Bracket Sequence, CF 1830C - Hyperregular Bracket Strings
Pattern 6: Counting Paths on a Grid
"How many ways to go from
Approach: Total steps
answer = C(n + m, n)Problems: CF 559C - Gerald and Giant Chess, CF 722E - Research Rover
Pattern 7: Multinomial / Product of Binomials
"How many distinct strings can be formed from given characters?" or "distribute items into multiple labeled groups of specified sizes."
Approach: fac[n] * inv_fac[k1] * ... * inv_fac[km] % MOD.
Problems: CF 1461D - Divide and Summarize, CF 1475D - Cleaning the Phone
Contest Cheat Sheet
+--------------------------------------------------------------+
| COMBINATORICS BASICS CHEAT SHEET |
+--------------------------------------------------------------+
| SETUP (paste at top of solution): |
| ll fac[MAXN], inv_fac[MAXN]; |
| // precompute() in main before queries |
+--------------------------------------------------------------+
| FORMULAS: |
| C(n,k) = fac[n] * inv_fac[k] % MOD * inv_fac[n-k] % MOD |
| Stars&Bars: C(n+k-1, k-1) [non-neg solutions] |
| Catalan: C(2n,n) - C(2n, n+1) |
| Derange: D(n) = (n-1)*(D(n-1) + D(n-2)) |
+--------------------------------------------------------------+
| PIE (m <= 20): |
| for mask in 0..2^m-1: |
| sign = (-1)^popcount(mask) |
| ans += sign * f(mask) |
+--------------------------------------------------------------+
| WATCH OUT: |
| - C(n,k) = 0 when k<0 or k>n |
| - 1LL * a * b % MOD (avoid int overflow) |
| - inv_fac via backward propagation (1 pow call total) |
| - Stars&Bars "at least 1" = C(n-1, k-1) |
+--------------------------------------------------------------+
| COMPLEXITY: |
| Precompute: O(n) | Query C(n,k): O(1) |
| Pascal DP: O(n^2) | PIE: O(2^m * f) |
+--------------------------------------------------------------+Gotchas & Debugging
Mental Traps
"I know the formula, I just plug in numbers." Combinatorics problems almost never say "compute
TRAP: Stars and Bars variants look identical but differ by 1
"distribute 10 candies among 3 kids"
├─ "each gets >= 0" -> C(10+3-1, 3-1) = C(12, 2) = 66
└─ "each gets >= 1" -> C(10-1, 3-1) = C(9, 2) = 36
ONE WORD changes the answer by almost 2x!"PIE is just alternating signs -- I can write it from memory." The sign pattern is easy. The hard part is computing
"Negative mod won't bite me -- I've seen the +MOD trick." You've seen it, but you'll forget it under contest pressure. The expression (a - b) % MOD is negative in C++ when a < b. Write (a - b % MOD + MOD) % MOD every single time -- make it a reflex, not a decision.
SIGN ERROR TRAP:
a = 3, b = 7, MOD = 5
WRONG: (3 - 7) % 5 = -4 % 5 = -4 (C++ behavior!)
RIGHT: (3 - 7 % 5 + 5) % 5 = (3 - 2 + 5) % 5 = 6 % 5 = 1Overflow in multiplication chain.
fac[n] * inv_fac[k] * inv_fac[n-k] % MODis wrong -- the first multiply overflowslong longbefore the mod. Writefac[n] % MOD * inv_fac[k] % MOD * inv_fac[n-k] % MODor chain with intermediate% MOD.MAXNtoo small. If the problem hasup to , you need MAXN = 2e6 + 5, not1e6 + 5. Off-by-one here gives wrong answers or segfaults.Forgetting
precompute(). Easy to forget in contest stress. Put it as the first line inmain().with or . Must return , not crash. Always guard in your C()function.Negative mod result.
(a - b) % MODcan be negative in C++. Always addMODbefore taking mod:(a - b % MOD + MOD) % MOD.Stars and Bars off-by-one. "At least 0 per bin" uses
. "At least 1 per bin" uses . Mixing these up is the #1 mistake. PIE sign error. Empty set (
) contributes , which is the total count without restrictions. Subset of size 1 gets . Double-check with a small example. Pascal's triangle memory. A
long longarray isMB -- use intand cast tolong longonly during addition, or use 1D rolling array if you only need one row.Catalan: integer division trap.
requires modular inverse of . Safer: use which avoids division entirely. Debugging PIE. Print the contribution of each bitmask. Verify against brute-force for
.
Common Mistakes
- Not precomputing inverse factorials.
C(n,k) mod pneedsfact[n] * inv_fact[k] % p * inv_fact[n-k] % p. Computing the inverse on-the-fly for each query is too slow. - Stars and Bars off-by-one. Distributing n identical items into k distinct bins =
C(n+k-1, k-1). UsingC(n+k, k)orC(n+k-1, n)instead gives the wrong formula. - Inclusion-exclusion sign errors. The sign alternates with subset size:
+for even,-for odd. A single sign flip invalidates the entire computation. - Overflow in Pascal's triangle.
C(n,k)grows fast. If not working modulo p, values overflowlong longfor n > ~60. - Forgetting the "at least 0" constraint. Stars and Bars counts non-negative distributions. For "at least 1 per bin," substitute
n' = n - kfirst. - Sizing factorial tables to the input, not the formula. On "Distributing Apples" (
apples, children, answer ) with , precomputing factorials only up to RTEs out of bounds on any test where the formula dereferences fact[n + k - 1]at index near. Always trace your formula to find the maximum argument to and size the table to that. For Stars and Bars it is , not just .
Debug Drills
Drill 1. Inverse factorial computed wrong.
cpp
inv_fact[0] = 1;
for (int i = 1; i <= N; i++)
inv_fact[i] = power(fact[i], MOD - 2, MOD); // What's the issue?What's wrong?
This is correct but O(N log MOD) -- computing N modular exponentiations. The fast way: compute inv_fact[N] = power(fact[N], MOD-2, MOD) once, then inv_fact[i] = inv_fact[i+1] * (i+1) % MOD backwards. This is O(N) + one exponentiation.
Drill 2. C(n, k) returns garbage for edge cases.
cpp
long long C(int n, int k) {
return fact[n] % MOD * inv_fact[k] % MOD * inv_fact[n - k] % MOD;
}
// Called with C(5, 7). What happens?What's wrong?
When k > n, n - k is negative, causing an out-of-bounds array access (or wrapping to a huge index). Add a guard: if (k < 0 || k > n) return 0;
Drill 3. Stars and Bars off-by-one.
cpp
// n identical balls into k distinct boxes, each box >= 0
long long stars_and_bars(int n, int k) {
return C(n + k, k); // What's wrong?
}What's wrong?
The correct formula is C(n + k - 1, k - 1). The - 1 comes from choosing positions for k - 1 dividers among n + k - 1 slots. Off-by-one here gives a systematically wrong answer.
Self-Test
Before moving to practice problems, verify you can do each of these without looking at the notes above:
- [ ] Write
precompute()andC(n, k)from memory, including backward propagation forinv_fac[] - [ ] State Stars and Bars for both variants (>= 0 and >= 1) and derive one from the other
- [ ] Compute
(derangements of 4 elements) by hand using PIE - [ ] Write the inclusion-exclusion bitmask loop with correct sign handling
- [ ] Explain why
fac[n] * inv_fac[k] * inv_fac[n-k] % MODoverflows butfac[n] % MOD * inv_fac[k] % MOD * inv_fac[n-k] % MODdoesn't - [ ] Given "distribute 7 identical balls into 4 distinct boxes, each box gets >= 2," reduce to Stars and Bars
Practice Problems
| Rating | You should be able to... |
|---|---|
| CF 1200 | Compute C(n, k) using Pascal's triangle for small n |
| CF 1500 | Precompute factorial and inverse factorial arrays; evaluate C(n, k) mod p in O(1) |
| CF 1800 | Apply Stars and Bars with upper-bound constraints (PIE); count lattice paths with obstacles |
| CF 2100 | Combine multinomial coefficients with generating functions; solve complex counting with DP + combinatorics |
| # | Problem | Source | Difficulty | Key Idea |
|---|---|---|---|---|
| 1 | Quasi Binary | CF 538B | Easy (1400) | Basic counting / representation |
| 2 | Two Arrays | CF 1288C | Easy (1600) | Stars and Bars |
| 3 | Colorful Bricks | CF 1081C | Easy (1500) | Binomial coefficient |
| 4 | Almost Identity Permutations | CF 888D | Medium (1600) | Derangement counting |
| 5 | Gerald and Giant Chess | CF 559C | Medium (1900) | Grid paths + PIE |
| 6 | Devu and Flowers | CF 451E | Medium (1900) | Stars and Bars + PIE with upper bounds |
| 7 | Beautiful Bracket Sequence | CF 1264D | Hard (2100) | Catalan / bracket counting |
| 8 | Hyperregular Bracket Strings | CF 1830C | Hard (2100) | Catalan over subsets |
| 9 | Binomial Coefficients | CSES | 1400 | Precompute factorials and inverse factorials |
| 10 | Creating Strings II | CSES | 1500 | Multinomial coefficient -- n! / (c1! * c2! * ...) |
| 11 | Distributing Apples | CSES | 1500 | Stars and bars: C(n+k-1, k-1) |
| 12 | Bracket Sequences I | CSES | 1700 | Catalan number application |
Further Reading
- cp-algorithms: Binomial Coefficients -- Complete reference with all computation methods.
- cp-algorithms: Catalan Numbers -- Derivation, applications, and generalizations.
- cp-algorithms: Inclusion-Exclusion -- Detailed treatment with worked examples.
- CF Blog: Combinatorics tutorial by pikmike -- Problem-focused, competition-oriented.
- CF Blog: Modular Arithmetic for Beginners -- Covers modular inverse, precomputation tricks.
Cross-references in this notebook:
- See: Number Theory -- modular inverse, Fermat's theorem (used for factorial precomputation).
Recap
- Precompute
fact[]andinv_fact[]up to MAXN for O(1) binomial coefficient queries mod p. - Stars and Bars: n identical items into k bins =
C(n+k-1, k-1). - Inclusion-exclusion: count
|A_1 union ... union A_k|via alternating sum of intersection sizes. - Pascal's recurrence is useful for small n or when you need the full triangle, but factorial-based computation is faster for individual queries.
Cross-Links
- Math Fundamentals -- modular exponentiation for computing inverse factorials
- Number Theory -- prime factorization feeds into combinatorial formulas
- Catalan Numbers -- a key combinatorial sequence built on binomial coefficients