容斥原理用於計算多個集合並集的大小,核心公式:
|A ∪ B| = |A| + |B| - |A ∩ B|
三集合擴展:
|A ∪ B ∪ C| = |A| + |B| + |C| - |A∩B| - |A∩C| - |B∩C| + |A∩B∩C|
通式:對於 n 個集合,奇數項相加,偶數項相減。
適用於 n ≤ 20 的集合數。每個子集對應一個 n 位二進制數,1 表示選取該集合。
// 假設有 m 個條件/集合
for (int mask = 1; mask < (1<
優點:代碼簡潔、通用、易於處理「至少滿足 k 個條件」等變體。
適用於條件間有依賴關係、或需剪枝的場景。
void dfs(int idx, int cnt, long long cur_lcm) {
if (idx == m) {
if (cnt == 0) return;
long long add = total / cur_lcm;
if (cnt % 2) ans += add; else ans -= add;
return;
}
dfs(idx+1, cnt, cur_lcm); // 不選
dfs(idx+1, cnt+1, lcm(cur_lcm, a[idx])); // 選
}
給定 a[1..m],求 |{x ∈ [1,n] : ∃i, x % a[i] == 0}|
long long solve(int n, vector& a) {
int m = a.size();
long long ans = 0;
for (int mask = 1; mask < (1<>i & 1) {
lcm = lcm / gcd(lcm, a[i]) * a[i];
if (lcm > n) { overflow = true; break; }
}
if (overflow) continue;
int bits = __builtin_popcount(mask);
if (bits & 1) ans += n / lcm;
else ans -= n / lcm;
}
return ans;
}
例:n 個球放入 m 個盒,每箱至少 1 個。
總方案 m^n,減去至少一個箱子為空的方案。
設 A_i 為第 i 個箱子為空,則合法方案 = m^n - |∪A_i|,用容斥計算並集。
|A_i| = (m-1)^n,|A_i ∩ A_j| = (m-2)^n...
答案 = Σ(-1)^k * C(m,k) * (m-k)^n
從 (0,0) 走到 (n,m),只能向右/向上,避開 k 個障礙點。
總路徑 = C(n+m, n)。
對每個障礙點子集,計算經過這些點的路徑數(按座標排序後分段相乘),容斥加減。
若集合條件互質(如質數),LCM 直接相乘,無溢出風險。
求「滿足所有條件」的個數 = 總數 - 滿足至少一個相反條件的個數。
有時反向條件更少,計算更快。
容斥原理是莫比烏斯反演在集合論上的體現。對於「整除」類問題,莫比烏斯函數 μ(d) 可直接給出係數,避免枚舉子集。
例:求 [1,n] 中與 m 互質的數 = Σ μ(d) * ⌊n/d⌋,d|m。
求最小不滿足的條件編號,可二分答案 + 容斥判定。
lcm > limit 直接跳過mask = 0 不參與容斥,對應總集合n=0 或集合為空時的處理| 題目 | 類型 | 關鍵點 |
|---|---|---|
| HDU 2476 / POJ 2398 | 整除計數 | LCM 容斥 |
| Codeforces 1740E | 排列組合 | 反向容斥 |
| Luogu P2556 | 網格路徑 | 障礙點子集枚舉 |
| BZOJ 2154 | 莫比烏斯優化 | μ 函數篩法 |
m ≤ 20 時首選多做幾道模板題,容斥思維就會肌肉記憶化。