注意事项:
涉及到位运算:java—位运算(二进制中1的个数
题目:
给定一个整数 n 和 m 个不同的质数 p1,p2,…,pm
请你求出 1∼n 中能被 p1,p2,…,pm 中的至少一个数整除的整数有多少个
第一行包含整数 n 和 m
第二行包含 m 个质数
输入:
10 2
2 3
输出:
7
public class 容斥原理_能被整除的数 {
public static int N = 20;
public static int[] p = new int[N];
public static void main(String[] args) {
//读入n, m和m个质数
Scanner in = new Scanner(System.in);
int n = in.nextInt(), m = in.nextInt();
for (int i = 0; i<m; i++) p[i] = in.nextInt();
int res = 0;
//注意这里是i是1起始,1左移m位就是2的m次方,我们总共枚举(2^m - 1)次, 这里是根据容斥原理公式得知
//t表示所有质数的乘积, count表示当前i的二进制包含几个1,也就是当前选法有几个集合
for (int i = 1; i < (1<<m); i++) {
int t = 1, count = 0;
//这里是用位运算来对比i的二进制的每一位,得到当前i的二进制中有几个1,也就是得到当前选法使用了几个集合
for (int j = 0; j < m; j++) {
if (((i >> j) & 1) == 1) {
if ((long)t * p[j] > n) { //如果t*p[j] > n,说明1∼n中的数不能被p整除,那就不用继续算了
t = -1;
break;
}
count++;
t *= p[j]; //将该质数乘到t中
}
}
//根据n/p能得到1~n中所有p的倍数的个数,那么扩展一下,n/(p1*p2*...*pk)就可以得到1~n中同时是p1,p2...pk的倍数的个数 (注意是整除)
//这里根据容斥原理公式: 奇数个集合是加,偶数个集合是减
if (t != -1) {
if (count % 2 != 0) res += n / t;
else res -= n / t;
}
}
System.out.println(res);
}
}
思路:
容斥原理公式:
⋃
i
=
1
m
S
i
=
S
1
+
S
2
+
…
+
S
m
−
(
S
1
⋂
S
2
+
S
1
⋂
S
3
+
…
+
S
m
−
1
⋂
S
m
)
+
(
S
1
⋂
S
2
⋂
S
3
+
…
+
S
m
−
2
⋂
S
m
−
1
⋂
S
m
)
+
…
+
(
−
1
)
m
−
1
(
⋂
i
=
1
m
S
)
\bigcup_{i=1}^{m} S_i = S_1 + S_2 + \ldots + S_m - (S_1 \bigcap S_2 + S_1 \bigcap S_3 +\ldots + S_{m-1} \bigcap S_m)+ (S_1 \bigcap S_2 \bigcap S_3 +\ldots + S_{m-2} \bigcap S_{m-1} \bigcap S_m) + \ldots + (-1)^{m - 1} (\bigcap_{i=1}^{m}S)
i=1⋃mSi=S1+S2+…+Sm−(S1⋂S2+S1⋂S3+…+Sm−1⋂Sm)+(S1⋂S2⋂S3+…+Sm−2⋂Sm−1⋂Sm)+…+(−1)m−1(i=1⋂mS)
容斥原理公式的直观应用(韦恩图求三个圆的不重叠面积):
一些证明:
以题目为栗子:
S
1
=
{
2
,
4
,
6
,
8
,
10
}
,
S
2
=
{
3
,
6
,
9
}
,
S
1
⋂
S
2
=
{
6
}
,
故
S
1
⋃
S
2
=
{
2
,
3
,
4
,
6
,
8
,
9
,
10
}
S_1 = \lbrace 2,4,6,8,10 \rbrace , S_2 = \lbrace 3,6,9 \rbrace, S_1 \bigcap S_2 = \lbrace 6 \rbrace, 故S_1 \bigcup S_2 = \lbrace 2,3,4,6,8,9,10 \rbrace
S1={2,4,6,8,10},S2={3,6,9},S1⋂S2={6},故S1⋃S2={2,3,4,6,8,9,10}
又是后半夜更新的一天~10月1号了哎,大家国庆假期快乐
声明:
算法思路来源为y总,详细请见https://www.acwing.com/
本文仅用作学习记录和交流