venn图:
如何求三个圆圈的面积之和?
此时,||不代表绝对值,代表集合的个数
解题思路:
实际上,我们不需要知道每个集合中的元素具体是什么,只需要知道每个集合的大小
例如 ,表示10以内能够被2整除的数有5个,每个集合的大小可以根据来确定。
那么的集合大小怎么算呢,其实就是
最后就是如何用代码表示每个集合状态(选或者没有选)?
在这里使用二进制,以 m = 4为例,需要四个二进制位来表示4个集合选中与不选的状态
1101这里表示选中集合S1,S2,S4,故这个集合中元素的个数为
观察公式不难得出选中集合的个数决定了是正或负,奇数为正,偶数为负
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int N = 20;
int n,m;
int p[N];
int main() {
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cin >> n >> m;
for(int i = 0; i < m; i++) cin >> p[i];
int res = 0;
//把i看成m位的二进制
// 举所有可能的子集
for(int i = 1; i < 1 << m; i++){
int t = 1;//乘积作为分母,求该集合元素的个数
int s = 0; //选中集合的个数
for(int j = 0; j < m; j++){
//第j个集合为1,表示选中
if(i >> j & 1){
if((ll) t * p[j] > n){
t = -1;
break;
}
s++;
t *= p[j];
}
}
if(t == -1) continue;// 跳过
if(s & 1) res += n / t;
else res -= n / t;
}
cout << res <<'\n';
return 0;
}
1. 外层循环的作用
外层循环 for(int i = 1; i < 1 << m; i++)
的目的是枚举所有可能的子集。在代码中,m
表示输入的 p
数组中的元素个数,也就是子集的元素个数。
1 << m
是 2^m
,代表有 m
个元素的集合所能产生的所有子集的数量。例如,如果 m = 3
,那么 1 << m
的值就是 2^3 = 8
,也就是 {000, 001, 010, 011, 100, 101, 110, 111}
,这8种情况分别代表不同的子集。
2.外层循环的过程
一个长度为 m
的数组 p
,我们要枚举所有 p
的子集。可以把 m
个元素的数组 p
的每一个子集看作一个长度为 m
的二进制数。这个二进制数中的每一位(0 或 1)表示对应位置的元素是否在子集中:
- 如果第
j
位是1
,表示p[j]
这个元素在子集中。 - 如果第
j
位是0
,表示p[j]
这个元素不在子集中。
举个例子
假设 p = [2, 3, 5]
,也就是 m = 3
。
对于每一个子集,我们可以用一个 3 位的二进制数来表示:
i = 0
(000): 空集{}
i = 1
(001): 子集{5}
i = 2
(010): 子集{3}
i = 3
(011): 子集{3, 5}
i = 4
(100): 子集{2}
i = 5
(101): 子集{2, 5}
i = 6
(110): 子集{2, 3}
i = 7
(111): 子集{2, 3, 5}
在二进制中,第 i
位为 1
表示选中了数组 p
中的第 i
个元素。
3.演示代码过程
输入和初始化
输入的 n = 10
,m = 2
,集合 p = {2, 3}
。程序初始化 res = 0
。
枚举子集
代码中的外层循环 for(int i = 1; i < 1 << m; i++)
会枚举所有的非空子集:
i = 1
对应二进制01
,表示子集{2}
i = 2
对应二进制10
,表示子集{3}
i = 3
对应二进制11
,表示子集{2, 3}
子集计算过程
-
子集
{2}
(i = 1
, 二进制01
):s = 1
(因为选中了1个元素)t = 2
(当前子集的乘积为2)- 计算
10 / 2 = 5
(1
到10
中能被2
整除的数有5
个)。 - 因为
s
是奇数,所以res += 5
,当前res = 5
。
-
子集
{3}
(i = 2
, 二进制10
):s = 1
(因为选中了1个元素)t = 3
(当前子集的乘积为3)- 计算
10 / 3 = 3
(1
到10
中能被3
整除的数有3
个)。 - 因为
s
是奇数,所以res += 3
,当前res = 8
。
-
子集
{2, 3}
(i = 3
, 二进制11
):s = 2
(因为选中了2个元素)t = 6
(2
和3
的乘积为6)- 计算
10 / 6 = 1
(1
到10
中能被6
整除的数有1
个)。 - 因为
s
是偶数,所以res -= 1
,当前res = 7
。
最终结果
最终,程序输出 res = 7
。这意味着在 1
到 10
之间,有 7
个数能被 2
或 3
整除。
这些数分别是:2, 3, 4, 6, 8, 9, 10
。