0x37 容斥原理与 Mobius 函数 容斥原理 设 S 1 , S 2 , … , S n S_{1}, S_{2}, \dots, S_{n} S 1 , S 2 , … , S n 为有限集合, ∣ S ∣ |S| ∣ S ∣ 表示集合 S S S 的大小,则:
∣ ⋃ i = 1 n S i ∣ = ∑ i = 1 n ∣ S i ∣ − ∑ 1 ≤ i < j ≤ n ∣ S i ∩ S j ∣ + ∑ 1 ≤ i < j < k ≤ n ∣ S i ∩ S j ∩ S k ∣ + … + ( − 1 ) n + 1 ∣ S 1 ∩ ⋯ ∩ S n ∣ \begin{array}{l} \left| \bigcup_ {i = 1} ^ {n} S _ {i} \right| = \sum_ {i = 1} ^ {n} | S _ {i} | - \sum_ {1 \leq i < j \leq n} | S _ {i} \cap S _ {j} | + \sum_ {1 \leq i < j < k \leq n} | S _ {i} \cap S _ {j} \cap S _ {k} | + \dots \\ + (- 1) ^ {n + 1} \left| S _ {1} \cap \dots \cap S _ {n} \right| \\ \end{array} ∣ ⋃ i = 1 n S i ∣ = ∑ i = 1 n ∣ S i ∣ − ∑ 1 ≤ i < j ≤ n ∣ S i ∩ S j ∣ + ∑ 1 ≤ i < j < k ≤ n ∣ S i ∩ S j ∩ S k ∣ + … + ( − 1 ) n + 1 ∣ S 1 ∩ ⋯ ∩ S n ∣ 我们可以简单地用文氏图来宏观地描述容斥原理,如下图所示。
多重集的组合数 设 S = { n 1 ⋅ a 1 , n 2 ⋅ a 2 , … , n k ⋅ a k } S = \{n_{1} \cdot a_{1}, n_{2} \cdot a_{2}, \dots, n_{k} \cdot a_{k}\} S = { n 1 ⋅ a 1 , n 2 ⋅ a 2 , … , n k ⋅ a k } 是由 n 1 n_{1} n 1 个 a 1 a_{1} a 1 , n 2 n_{2} n 2 个 a 2 a_{2} a 2 , … \dots … , n k n_{k} n k 个 a k a_{k} a k 组成的多重集。设 n = ∑ i = 1 k n i n = \sum_{i=1}^{k} n_{i} n = ∑ i = 1 k n i , 对于任意整数 r ≤ n r \leq n r ≤ n , 从 S S S 中取出 r r r 个元素组成一个多重集 (不考虑顺序), 产生的不同多重集的数量为:
C k + r − 1 k − 1 − ∑ i = 1 k C k + r − n i − 2 k − 1 + ∑ 1 ≤ i < j ≤ k C k + r − n i − n j − 3 k − 1 − ⋯ + ( − 1 ) k C k + r − ∑ i = 1 k n i − ( k + 1 ) k − 1 C _ {k + r - 1} ^ {k - 1} - \sum_ {i = 1} ^ {k} C _ {k + r - n _ {i} - 2} ^ {k - 1} + \sum_ {1 \leq i < j \leq k} C _ {k + r - n _ {i} - n _ {j} - 3} ^ {k - 1} - \dots + (- 1) ^ {k} C _ {k + r - \sum_ {i = 1} ^ {k} n _ {i} - (k + 1)} ^ {k - 1} C k + r − 1 k − 1 − i = 1 ∑ k C k + r − n i − 2 k − 1 + 1 ≤ i < j ≤ k ∑ C k + r − n i − n j − 3 k − 1 − ⋯ + ( − 1 ) k C k + r − ∑ i = 1 k n i − ( k + 1 ) k − 1 证明:
不考虑 n i n_i n i 的限制,从 S S S 中任选 r r r 个元素,相当于从多重集 { ∞ ⋅ a 1 , ∞ ⋅ a 2 , … , ∞ ⋅ a k } \{\infty \cdot a_1, \infty \cdot a_2, \dots, \infty \cdot a_k\} { ∞ ⋅ a 1 , ∞ ⋅ a 2 , … , ∞ ⋅ a k } 中取出 r r r 个元素。根据我们在0x36节中对多重集组合数的讨论,方法数为 C k + r − 1 k − 1 C_{k+r-1}^{k-1} C k + r − 1 k − 1 。
设 S i S_{i} S i ( 1 ≤ i ≤ k ) (1\leq i\leq k) ( 1 ≤ i ≤ k ) 表示至少包含 n i + 1 n_i + 1 n i + 1 个 a i a_{i} a i 的多重集。我们先从 s s s 中取出 n i + 1 n_i + 1 n i + 1 个 a i a_{i} a i ,然后再任选 r − n i − 1 r - n_{i} - 1 r − n i − 1 个元素,即可构成 S i S_{i} S i 。与上面同理,可以构成的不同 S i S_{i} S i 的数量为 C k + r − n i − 2 k − 1 C_{k + r - n_i - 2}^{k - 1} C k + r − n i − 2 k − 1
进一步地,先从 S S S 中取出 n i + 1 n_i + 1 n i + 1 个 a i a_i a i 和 n j + 1 n_j + 1 n j + 1 个 a j a_j a j ,然后再任选 r − n i − n j − 2 r - n_i - n_j - 2 r − n i − n j − 2 个元素,即可构成 S i ∩ S j S_i \cap S_j S i ∩ S j ,方法数为:
C k + r − n i − n j − 3 k − 1 C _ {k + r - n _ {i} - n _ {j} - 3} ^ {k - 1} C k + r − n i − n j − 3 k − 1 根据容斥原理,至少有一种 a i a_{i} a i 选取的数量超过 n i n_i n i 限制的多重集共有:
∣ ⋃ i = 1 k S i ∣ = ∑ i = 1 k C k + r − n i − 2 k − 1 − ∑ 1 ≤ i < j ≤ k C k + r − n i − n j − 3 k − 1 + ⋯ + ( − 1 ) k + 1 C k + r − ∑ i = 1 k n i − ( k + 1 ) k − 1 \left| \bigcup_ {i = 1} ^ {k} S _ {i} \right| = \sum_ {i = 1} ^ {k} C _ {k + r - n _ {i} - 2} ^ {k - 1} - \sum_ {1 \leq i < j \leq k} C _ {k + r - n _ {i} - n _ {j} - 3} ^ {k - 1} + \dots + (- 1) ^ {k + 1} C _ {k + r - \sum_ {i = 1} ^ {k} n _ {i} - (k + 1)} ^ {k - 1} i = 1 ⋃ k S i = i = 1 ∑ k C k + r − n i − 2 k − 1 − 1 ≤ i < j ≤ k ∑ C k + r − n i − n j − 3 k − 1 + ⋯ + ( − 1 ) k + 1 C k + r − ∑ i = 1 k n i − ( k + 1 ) k − 1 故满足所有限制的合法多重集共有 C k + r − 1 k − 1 − ∣ ⋃ i = 1 k S i ∣ C_{k + r - 1}^{k - 1} - \left|\bigcup_{i = 1}^{k}S_{i}\right| C k + r − 1 k − 1 − ⋃ i = 1 k S i 个,两式相减即得原命题。证毕。
【例题】Devu and Flowers Codeforces451E Devu 有 N N N 个盒子,第 i i i 个盒子中有 A i A_{i} A i 枝花。同一个盒子内的花颜色相同,不同盒子内的花颜色不同。Devu 要从这些盒子中选出 M M M 枝花组成一束,求共有多少种方案。若两束花每种颜色的花的数量都相同,则认为这两束花是相同的方案。输出对 10 9 + 7 10^{9} + 7 1 0 9 + 7 取模之后的结果即可。 1 ≤ N ≤ 20 , 0 ≤ M ≤ 10 14 , 0 ≤ A i ≤ 10 12 1 \leq N \leq 20, 0 \leq M \leq 10^{14}, 0 \leq A_{i} \leq 10^{12} 1 ≤ N ≤ 20 , 0 ≤ M ≤ 1 0 14 , 0 ≤ A i ≤ 1 0 12 。
设第 i i i 个盒子中的花颜色为 C i C_i C i ,则本题等价于从多重集 S = { A 1 ⋅ C 1 , A 2 ⋅ C 2 , … , A n ⋅ C n } S = \{A_1 \cdot C_1, A_2 \cdot C_2, \dots, A_n \cdot C_n\} S = { A 1 ⋅ C 1 , A 2 ⋅ C 2 , … , A n ⋅ C n } 中选出 M M M 个元素能够产生的不同多重集的数量。根据多重集组合数的结论,本题的答案为:
C N + M − 1 N − 1 − ∑ i = 1 N C N + M − A i − 2 N − 1 + ∑ 1 ≤ i < j ≤ N C N + M − A i − A j − 3 N − 1 − ⋯ + ( − 1 ) N C N + M − ∑ i = 1 N C i − ( N + 1 ) N − 1 C _ {N + M - 1} ^ {N - 1} - \sum_ {i = 1} ^ {N} C _ {N + M - A _ {i} - 2} ^ {N - 1} + \sum_ {1 \leq i < j \leq N} C _ {N + M - A _ {i} - A _ {j} - 3} ^ {N - 1} - \dots + (- 1) ^ {N} C _ {N + M - \sum_ {i = 1} ^ {N} C _ {i} - (N + 1)} ^ {N - 1} C N + M − 1 N − 1 − i = 1 ∑ N C N + M − A i − 2 N − 1 + 1 ≤ i < j ≤ N ∑ C N + M − A i − A j − 3 N − 1 − ⋯ + ( − 1 ) N C N + M − ∑ i = 1 N C i − ( N + 1 ) N − 1 在具体实现时,我们可以枚举 x = 0 ∼ 2 N − 1 x = 0\sim 2^{N} - 1 x = 0 ∼ 2 N − 1 ,若 x \pmb{x} x 在二进制表示下共有 P \mathcal{P} P 位为
1,分别是第 i 1 , i 2 , … , i p i_1,i_2,\dots ,i_p i 1 , i 2 , … , i p 位,则这个 X \mathcal{X} X 就代表上式中的一项:
( − 1 ) p C N + M − C i 1 − C i 2 − … ⋯ − C i p − ( p + 1 ) (- 1) ^ {p} C _ {N + M - C _ {i _ {1}} - C _ {i _ {2}} - \dots \dots - C _ {i _ {p} - (p + 1)}} ( − 1 ) p C N + M − C i 1 − C i 2 − …⋯ − C i p − ( p + 1 ) 特别地, x = 0 x = 0 x = 0 代表 C N + M − 1 N C_{N + M - 1}^{N} C N + M − 1 N 。这样我们就可以得到容斥原理计算多重集组合数的公式的每一项。
对于每一项组合数, N N N 的范围很小,但 M M M 的范围很大。我们以 C N + M − 1 N − 1 C_{N + M - 1}^{N - 1} C N + M − 1 N − 1 为例,因为 C N + M − 1 N − 1 = P N + M − 1 N − 1 / ( N − 1 ) ! C_{N + M - 1}^{N - 1} = P_{N + M - 1}^{N - 1} / (N - 1)! C N + M − 1 N − 1 = P N + M − 1 N − 1 / ( N − 1 )! ,所以可以先计算排列数 P N + M − 1 N − 1 = ( N + M − 1 ) ∗ ( N + M − 2 ) ∗ ⋯ ∗ ( M + 1 ) P_{N + M - 1}^{N - 1} = (N + M - 1) * (N + M - 2) * \dots * (M + 1) P N + M − 1 N − 1 = ( N + M − 1 ) ∗ ( N + M − 2 ) ∗ ⋯ ∗ ( M + 1 ) ,然后再乘上 ( N − 1 ) ! (N - 1)! ( N − 1 )! 的乘法逆元。我们还可以应用Lucas定理,先把 N + M − 1 N + M - 1 N + M − 1 对 10 9 + 7 10^9 + 7 1 0 9 + 7 取模,再计算组合数,避免乘法结果超出64位整数的表示范围。
const int mod = 1000000007;
long long a[22], m, ans = 0;
int inv[22], n;
int power(int a, int b) { // a^b
int c = 1;
for ( ; b; b >= 1 ) {
if (b&1) c = (long long)c * a % mod;
a = (long long)a * a % mod;
}
return c;
}
// C(y,x)
int C(long long y, int x) {
if (y < 0 || x < 0 || y < x) return 0;
y % = mod;
if (y == 0 || x == 0) return 1;
int ans = 1;
for (int i = 0; i <= x; i++)
ans = (long long)ans * (y - i) % mod;
for (int i = 1; i <= x; i++)
ans = (long long)ans * inv[i] % mod;
return ans;int main() {
for (int i = 1; i <= 20; i++)
inv[i] = power(i, mod-2);
cin >> n >> m;
for (int i = 1; i <= n; i++) cin >> a[i];
for (int x = 0; x < 1 << n; x++) {
if (x == 0) {
ans = (ans + C(n+m-1, n-1)) % mod;
}
else {
long long t = n + m;
int p = 0;
for (int i = 0; i < n; i++)
if (x >> i & 1) {
p++;
t -= a[i + 1];
}
t -= p + 1;
if (p&1) {
ans = (ans - C(t, n-1)) % mod;
}
else {
ans = (ans + C(t, n-1)) % mod;
}
}
cout << (ans + mod) % mod << endl;Mobius函数 设正整数 N N N 按照算术基本定理分解质因数为 N = p 1 c 1 p 2 c 2 … p m c m N = p_{1}^{c_{1}}p_{2}^{c_{2}}\dots p_{m}^{c_{m}} N = p 1 c 1 p 2 c 2 … p m c m ,定义函数
μ ( N ) = { 0 ∃ i ∈ [ 1 , m ] , c i > 1 1 m ≡ 0 ( m o d 2 ) , ∀ i ∈ [ 1 , m ] , c i = 1 − 1 m ≡ 1 ( m o d 2 ) , ∀ i ∈ [ 1 , m ] , c i = 1 \mu (N) = \left\{ \begin{array}{l l} 0 & \quad \exists i \in [ 1, m ], c _ {i} > 1 \\ 1 & \quad m \equiv 0 (\mathrm {m o d} 2), \forall i \in [ 1, m ], c _ {i} = 1 \\ - 1 & \quad m \equiv 1 (\mathrm {m o d} 2), \forall i \in [ 1, m ], c _ {i} = 1 \end{array} \right. μ ( N ) = ⎩ ⎨ ⎧ 0 1 − 1 ∃ i ∈ [ 1 , m ] , c i > 1 m ≡ 0 ( mod 2 ) , ∀ i ∈ [ 1 , m ] , c i = 1 m ≡ 1 ( mod 2 ) , ∀ i ∈ [ 1 , m ] , c i = 1 称 μ ( N ) \mu (N) μ ( N ) 为Mobius函数(莫比乌斯函数)。
通俗地讲,当 N N N 包含相等的质因子时, μ ( N ) = 0 \mu (N) = 0 μ ( N ) = 0 。当 N N N 的所有质因子各不相等时,若 N N N 有偶数个质因子, μ ( N ) = 1 \mu (N) = 1 μ ( N ) = 1 ,若 N N N 有奇数个质因子, μ ( N ) = 0 \mu (N) = 0 μ ( N ) = 0 。
若只求一项Mobius函数,则分解质因数即可计算。若求 1 ∼ N 1\sim N 1 ∼ N 的每一项Mobius函数,可以利用Eratosthenes筛法计算。把所有 μ \mu μ 值初始化为1。接下来,对于筛出的每个质数 p p p ,令 μ ( p ) = − 1 \mu (p) = -1 μ ( p ) = − 1 ,并扫描 p p p 的倍数 x = 2 p , 3 p , … , [ n / p ] ∗ p x = 2p,3p,\dots ,[n / p]*p x = 2 p , 3 p , … , [ n / p ] ∗ p ,检查 x x x 能否被 p 2 p^2 p 2 整除。若能,则令 μ ( x ) = 0 \mu (x) = 0 μ ( x ) = 0 ,否则令 μ ( x ) = − μ ( x ) \mu (x) = -\mu (x) μ ( x ) = − μ ( x ) 。
for (int i = 1; i <= n; i++) miu[i] = 1, v[i] = 0;
for (int i = 2; i <= n; i++) {
if (v[i]) continue;
miu[i] = -1;
for (int j = 2 * i; j <= n; j++) {
v[j] = 1;
if ((j / i) % i == 0) miu[j] = 0;
else miu[j] *= -1;
}
}【例题】ZapBZOJ1101
有 50000 组询问,每次询问给定三个整数 a , b , k a, b, k a , b , k ,求有多少对二元组 ( x , y ) (x, y) ( x , y ) ,满足 x ≤ a x \leq a x ≤ a , y ≤ b y \leq b y ≤ b 并且 gcd ( a , b ) = k \gcd(a, b) = k g cd( a , b ) = k 。 1 ≤ k ≤ a , b ≤ 50000 1 \leq k \leq a, b \leq 50000 1 ≤ k ≤ a , b ≤ 50000 。
求有多少对二元组 ( x , y ) (x, y) ( x , y ) 满足 x ≤ a , y ≤ b x \leq a, y \leq b x ≤ a , y ≤ b 并且 gcd ( a , b ) = k \gcd(a, b) = k g cd( a , b ) = k ,等价于求有多少对二元组 ( x , y ) (x, y) ( x , y ) 满足 x ≤ a / k , y ≤ b / k x \leq a / k, y \leq b / k x ≤ a / k , y ≤ b / k 并且 x , y x, y x , y 互质。
设 D [ a , b , k ] D[a,b,k] D [ a , b , k ] 表示满足 x ≤ a , y ≤ b x \leq a, y \leq b x ≤ a , y ≤ b 且 k ∣ gcd ( a , b ) k \mid \gcd(a,b) k ∣ g cd( a , b ) 的二元组有多少对。显然只要 x , y x,y x , y 都是 k k k 的倍数即可。 1 ∼ a 1 \sim a 1 ∼ a 之间 k k k 的倍数有 ⌊ a / k ⌋ \lfloor a / k \rfloor ⌊ a / k ⌋ 个。故 D [ a , b , k ] = ⌊ a / k ⌋ ⌊ b / k ⌋ D[a,b,k] = \lfloor a / k \rfloor \lfloor b / k \rfloor D [ a , b , k ] = ⌊ a / k ⌋ ⌊ b / k ⌋ 。
设 F [ a , b ] F[a,b] F [ a , b ] 表示满足 x ≤ a , y ≤ b x \leq a, y \leq b x ≤ a , y ≤ b 且 x , y x,y x , y 互质的二元组有多少对。根据容斥原理:
F [ a , b ] = ∑ i = 1 min ( a , b ) μ ( i ) ∗ D [ a , b , i ] F [ a, b ] = \sum_ {i = 1} ^ {\min (a, b)} \mu (i) * D [ a, b, i ] F [ a , b ] = i = 1 ∑ m i n ( a , b ) μ ( i ) ∗ D [ a , b , i ] 上式的意思是,没有任何限制的二元组总数为 D [ a , b , 1 ] = a ∗ b D[a, b, 1] = a * b D [ a , b , 1 ] = a ∗ b 。应当减去 gcd ( a , b ) \gcd(a, b) g cd( a , b ) 是2,3,5,…的倍数的二元组数量。这样又重复减掉了 gcd ( a , b ) \gcd(a, b) g cd( a , b ) 既是2的倍数、又是3的倍数的二元组数量,应该加回来。依此类推, D [ a , b , i ] D[a, b, i] D [ a , b , i ] 的系数恰好就是Mobius函数。
回顾 0 × 32 0 \times 32 0 × 32 节中例题“余数之和”的讨论,我们知道: ∀ i ∈ [ x , min ( ⌊ a / ⌊ a / x ⌋ ] , ⌊ b / ⌊ b / x ⌋ ⌋ ) \forall i \in [x, \min \left( \lfloor a / \lfloor a / x \rfloor \right], \lfloor b / \lfloor b / x \rfloor \rfloor) ∀ i ∈ [ x , min ( ⌊ a / ⌊ a / x ⌋ ] , ⌊ b / ⌊ b / x ⌋⌋) , D [ a , b , i ] = ⌊ a / i ⌋ ⌊ b / i ⌋ D[a, b, i] = \lfloor a / i \rfloor \lfloor b / i \rfloor D [ a , b , i ] = ⌊ a / i ⌋ ⌊ b / i ⌋ 的值都相等,预处理出 Möbius 函数的前缀和,即可直接累加这一段的答案。这样的段只有 O ( a + b ) O\left( \sqrt{a} + \sqrt{b} \right) O ( a + b ) 个。