组合数学


容斥原理

集合 SS 的子集 A1A_1 有性质 p1p_1A2A_2 有性质 P2P_2\dotsAnA_n 有性质 PnP_n
那么,集合 SS 中具有性质 P1,P2,,PnP_1,P_2,\dots,P_n 的集合个数为

i=1nAi=i=1nAi1ijnAiAj+1ijknAiAjAk+(1)n+1i=1nAi\Big|\bigcap_{i=1}^nA_i \Big| = \sum_{i=1}^n\left| A_i\right| - \sum_{1 \leq i \leq j \leq n} \left| A_i \cap A_j \right| + \sum_{1 \leq i \leq j \leq k \leq n} \left| A_i \cap A_j \cap A_k \right| - \dots + (-1)^{n+1} \left| \cap_{i=1}^n A_i \right|

那么不具有性质 P1,P2,,PnP_1,P_2,\dots,P_n 的集合个数呢?很明显就是

Si=1nAi\Big| S \Big| - \Big| \bigcap_{i=1}^n A_i \Big|


来个例子加深理解

问题:在100人中,60人喜欢咖啡,40人喜欢茶,20人两者都喜欢。求至少喜欢一种饮料的人数?一种饮料都不喜欢的人数?

解答:
AA 为喜欢咖啡的集合,BB 为喜欢茶的集合。
容斥原理给出:

AB=A+BAB=60+4020=80. |A \cup B| = |A| + |B| - |A \cap B| = 60 + 40 - 20 = 80.

因此,80人喜欢至少一种饮料。

SA=SAB=10080=20.|\complement_S A| = |S| - |A \cup B| = 100 - 80 = 20.

因此,20人一种饮料都不喜欢


以下是一道例题(2024CCPC新疆E题):


题目

Bob现在具有一个长为 nn 的序列 a1,a2,a3,,ana_1,a_2,a_3,\cdots,a_n

一共有 qq 次询问,每次给定一个数 xx

询问在 [1,x][1,x] 中有多少个 正整数 满足它不是任何 aia_i 的倍数。

输出满足这样要求的正整数数量。

输入

第一行一个正整数 nn,表示数组的长度。

第二行 nn 个正整数,第 ii 个正整数表示 aia_i

第三行一个正整数 qq,表示共有qq个询问。

接下来的 qq 行中,第 ii 行一个正整数 xx 表示当次询问。

其中保证n10,q104,ai,x109n \leq 10,q \leq 10^4, a_i, x \leq10^9.


子集共有 2n2^n 个,用二进制法把每个子集给枚举出来,奇数个子集相加,偶数个子集减去,本题属于"不是、不具有"的情况,因此满足“不具有”的情况就是总集减去子集数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include <bits/stdc++.h>
using namespace std;
using ull = unsigned long long;

int n,q,a[15];

inline ull lcm(int x,int y){
return x/__gcd(x,y)*y;
}

int main(void){
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
cin >> n;
for(int i=1;i<=n;i++){
cin >> a[i];
}
cin >> q;
while(q--){
ull x, ans = 0;
cin >> x;
for(int i = 1; i < (1 << n); i++){
ull sum = 0, glcm = 1;
for(int j=0;j<n;j++){
if((i>>j) & 1){
sum++;
glcm = lcm(glcm,a[j+1]);
}
}
if(sum & 1) ans += x/glcm;
else ans -= x/glcm;
}
cout << x - ans << '\n';
}
return 0;
}

鸽巢原理

鸽巢原理,即抽屉原理,其主要内容是: 把 n+1 个物体放进 n 个盒子,至少有一个盒子包含两个或更多物体.

题目

以HDU 1205举例

这题如果去模拟的话还是比较麻烦,但是如果用鸽巢原理就很简单.

隔板法: 找出最多数量的那种糖果, 把它看成n个隔板,每个隔板的右边放置其他糖果.

令其他糖果总数为s,

如果 s< n-1 ,一定无解,这会使两个隔板之间没有其他糖果

如果 s>= n-1,一定有解,因为隔板的数量比剩下每种糖果的数量都多,因此不会有相邻同种糖果.

(看似简单的一题通过率仅28.5% , 这题有坑! 可能出现1e6 * 1e6的情况 , 不得不#define int long long)

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <bits/stdc++.h>
using namespace std;
#define int long long

signed main(void){
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
int t,n;
cin >> t;
while(t--){
cin >> n;
int mx=0,s=0,ai;
for(int i=0;i<n;i++){
cin >> ai;
mx=max(mx,ai);
s+=ai;
}
s-=mx;
cout << ((s<mx-1)?"No\n":"Yes\n");
}
return 0;
}