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.

ProblemSets 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 colourssame as surjections
permutations avoiding forbidden positionsrook numbers of the board
lattice paths avoiding cellspaths through each chosen cellDP over cells sorted by position
-tuples with in Möbius form
distinct values in a rangeMöbius-weighted counting
When terms are too manyTechnique
sets indexed by divisorsMöbius function: instead of (Arithmetic Functions)
depends only on collapse to (derangements, surjections): the most common escape
sets form a lattice or posetMöbius function of the poset (below)
a value for every maskSOS / subset-sum DP, : SOS DP
only a few sets overlaphandle 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;
}

Derangements

Permutations with no fixed point: = 1, 0, 1, 2, 9, 44, 265, 1854, 14833, 133496, …

FactFormula
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 pointsexactly 1 for every (linearity: each element is fixed with probability )
EGF (Generating Functions)
Related countAnswer
permutations with all cycles of length derangements
involutions (): 1, 1, 2, 4, 10, 26, 76
permutations with exactly cyclesStirling 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;
}

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;
}