Count a union by adding the parts, subtracting double-counted pairs, adding back triples, and so on; more usefully, count the objects satisfying none of a set of conditions by counting those satisfying each subset of them, since intersections are simple constraints:
It is the bridge to Möbius inversion, SOS DP and the derangement family. Bonferroni inequalities: truncating the alternating sum after an odd number of terms overestimates the union, after an even number underestimates it.
| Problem | Sets | Result |
|---|---|---|
| integers in coprime to | divisible by the -th prime factor of | Euler’s totient for |
| integers in divisible by none of | divisible by ; intersections use lcm | |
| derangements | ” is a fixed point” | |
| surjections | “box is empty” | |
| colourings using all colours | same as surjections | |
| permutations avoiding forbidden positions | rook numbers of the board | |
| lattice paths avoiding cells | paths through each chosen cell | DP over cells sorted by position |
| -tuples with in | Möbius form | |
| distinct values in a range | Möbius-weighted counting |
| When terms are too many | Technique |
|---|---|
| sets indexed by divisors | Möbius function: instead of (Arithmetic Functions) |
| depends only on | collapse to (derangements, surjections): the most common escape |
| sets form a lattice or poset | Möbius function of the poset (below) |
| a value for every mask | SOS / subset-sum DP, : SOS DP |
| only a few sets overlap | handle connected components of the overlap graph separately |
ll countDivisibleByNone(ll n, const vector<ll>& d) { // #x in [1, n] divisible by no d_i, O(2^k k)
int k = sz(d); ll total = 0;
for (int mask = 0; mask < (1 << k); mask++) {
ll l = 1; bool big = false;
for (int i = 0; i < k && !big; i++) if (mask >> i & 1) {
ll g = gcd(l, d[i]);
if (l / g > n / d[i]) big = true; else l = l / g * d[i]; // lcm > n contributes 0
}
if (!big) total += (__builtin_popcount(mask) % 2 ? -1 : 1) * (n / l);
}
return total;
}
mint surjections(ll n, int k, const Comb& cb) { // functions [n] -> [k] hitting every box
mint res = 0;
for (int j = 0; j <= k; j++) { mint t = cb.C(k, j) * mint(k - j).pow(n); res += j % 2 ? -t : t; }
return res;
}
vector<int> mobiusSieve(int n) { // mu[1..n], linear sieve
vector<int> mu(n + 1, 0), primes; vector<char> comp(n + 1, 0); mu[1] = 1;
for (int i = 2; i <= n; i++) {
if (!comp[i]) primes.push_back(i), mu[i] = -1;
for (int p : primes) { if ((ll)i * p > n) break; comp[i * p] = 1; if (i % p == 0) { mu[i * p] = 0; break; } mu[i * p] = -mu[i]; }
}
return mu;
}
ll coprimeTuples(int n, int k, const vector<int>& mu) { // #(x_1..x_k) in [1, n]^k with gcd 1 (fits ll for small k)
ll res = 0;
for (int d = 1; d <= n; d++) if (mu[d]) { ll t = 1; for (int i = 0; i < k; i++) t *= n / d; res += mu[d] * t; }
return res;
}Proof: why the signs work, and Bonferroni
Fix an element lying in exactly of the sets. In the complement formula it is counted once for every subset of those sets, with sign : . So elements in no set count 1 and all others 0. Bonferroni: truncating after the terms with gives an element in exactly sets the weight (alternating partial sum), whose sign is ; so the truncated complement count is too large for even and too small for odd , and the union bounds alternate correspondingly. Divisibility: is divisible by all , , iff , and there are such ; an lcm above contributes 0, which also avoids overflow. Any has at most 15 distinct prime factors (), so prime-factor enumerations are small. Möbius form: , so ; this is inclusion–exclusion over the prime divisors, with the sign of the squarefree set of primes. Surjections: . Checked against brute force.
Derangements
Permutations with no fixed point: = 1, 0, 1, 2, 9, 44, 265, 1854, 14833, 133496, …
| Fact | Formula |
|---|---|
| inclusion–exclusion | |
| recurrences | , |
| nearest integer | (): a random permutation is a derangement with probability (already 3 decimals at : the hat-check problem) |
| exactly fixed points | ; summing over : |
| expected fixed points | exactly 1 for every (linearity: each element is fixed with probability ) |
| EGF | (Generating Functions) |
| Related count | Answer |
|---|---|
| permutations with all cycles of length | derangements |
| involutions () | : 1, 1, 2, 4, 10, 26, 76 |
| permutations with exactly cycles | Stirling numbers of the first kind (Stirling and Bell) |
| no and no () | |
| ménage problem (seat couples around a table alternating men and women, nobody next to their partner; men’s seats fixed) | Touchard: : 1, 0, 0, 1, 2, 13, 80, 579 for (times for the full seating) |
| a random permutation has a cycle longer than | probability : the 100 prisoners analysis |
Forbidden positions in general. Permutations avoiding an arbitrary set of forbidden cells are counted by the permanent of the 0/1 allowed matrix (#P-hard in general; equivalently counting perfect matchings of a bipartite graph). Small : Ryser’s formula in . Structured boards: rook polynomials, where counts placements of non-attacking rooks on (for the diagonal, : derangements; for the two diagonals : ; staircase boards: ménage-like formulas).
mint derangement(int n) { if (n == 0) return 1; mint a = 1, b = 0; for (int i = 2; i <= n; i++) { mint c = mint(i - 1) * (a + b); a = b; b = c; } return n == 1 ? 0 : b; }
mint menage(int n, const Comb& cb) { // Touchard, n >= 2 (men fixed); M_0 = 1, M_1 = 0
if (n == 0) return 1;
if (n == 1) return 0;
mint res = 0;
for (int k = 0; k <= n; k++) { mint t = mint(2 * n) / mint(2 * n - k) * cb.C(2 * n - k, k) * cb.f[n - k]; res += k % 2 ? -t : t; }
return res;
}
ll permanentRyser(const vector<vector<ll>>& a) { // per(A) = (-1)^n sum_S (-1)^|S| prod_i sum_{j in S} a_ij, O(2^n n) with Gray code
int n = sz(a); if (n == 0) return 1;
vector<ll> rowSum(n, 0); ll total = 0; int prev = 0;
for (int g = 1; g < (1 << n); g++) {
int cur = g ^ (g >> 1), diff = cur ^ prev, j = __builtin_ctz(diff); bool add = cur & diff;
for (int i = 0; i < n; i++) rowSum[i] += add ? a[i][j] : -a[i][j];
ll prod = 1; for (int i = 0; i < n; i++) prod *= rowSum[i];
total += (__builtin_popcount(cur) % 2 ? -1 : 1) * prod;
prev = cur;
}
return n % 2 ? -total : total;
}Proof: the derangement formulas, ménage and Ryser
Inclusion–exclusion: with = ” fixed”, . First recurrence: element 1 goes to some ( choices); if goes to 1, the other elements form a derangement (); otherwise rename “position 1” as ‘s forbidden spot and the elements other than 1 form a derangement (). Second recurrence: from the explicit sum, . Nearest integer: . Exactly fixed points: choose them and derange the rest. Long cycle: a permutation has at most one cycle longer than , and the number with a cycle of length exactly is , so the probability is .
Rook polynomials: let = “the permutation uses forbidden cell ”; a set of cells can all be used only if they are non-attacking, and then completions remain; grouping by gives . For “no or ” the forbidden cells form a path of cells in which consecutive cells attack, and pairwise non-consecutive cells among can be chosen in ways. Ménage: with the men seated, the women’s forbidden cells form a cycle of cells (each woman cannot sit on either side of her husband), and non-adjacent cells on a -cycle can be chosen in ways, which gives Touchard’s formula.
Ryser: expand as a sum over functions ; by inclusion–exclusion over the columns that misses, keeps exactly the surjective, i.e. bijective, , whose total is the permanent. The Gray code changes one column per step, so each row sum updates in . All functions were checked against brute force over permutations for .
Möbius inversion on posets
For a locally finite poset, and for (0 if ). Then
Special cases: a chain gives differences (); the subset lattice gives , i.e. inclusion–exclusion and the SOS Möbius transform; the divisor lattice gives the number-theoretic ; the partition lattice gives for blocks; a product of posets has the product Möbius function. In the incidence algebra (functions on pairs with ), is the inverse of the zeta function .
vector<vector<ll>> posetMobius(const vector<vector<char>>& le) { // le[x][y]: x <= y; elements in a linear extension order
int n = sz(le); vector<vector<ll>> mu(n, vector<ll>(n, 0));
for (int x = 0; x < n; x++) {
mu[x][x] = 1;
for (int y = x + 1; y < n; y++) if (le[x][y]) {
ll s = 0; for (int z = x; z < y; z++) if (le[x][z] && le[z][y]) s += mu[x][z];
mu[x][y] = -s;
}
}
return mu;
}Proof: Möbius inversion on a poset
By definition , i.e. in the incidence algebra; for finite posets a one-sided inverse is two-sided (triangular matrices in a linear extension), so also : . If then ; the converse is the same computation with . Subset lattice: the interval is a Boolean lattice of rank and verifies the formula. Products: satisfies the defining recursion because sums over a product interval factor. The divisor lattice of is a product of chains, giving the classical . Checked on divisor and subset lattices.
Related notes
- Arithmetic Functions (Möbius)
- Submasks and SOS DP
- Bipartite Matching (permanents count perfect matchings)