76.Acwing基础课第890题-简单-能被整除的数
76.Acwing基础课第890题-简单-能被整除的数
题目描述
\(给定一个整数 n 和 m 个不同的质数 p_1,p_2,…,p_m\)。
\(请你求出 1∼n 中能被 p_1,p_2,…,p_m 中的至少一个数整除的整数有多少个\)。
输入格式
\(第一行包含整数 n 和 m\)。
\(第二行包含 m 个质数\)。
输出格式
\(输出一个整数,表示满足条件的整数的个数\)。
数据范围
\(1≤m≤16\),
\(1≤n,p_i≤10^9\)
输入样例:
10 2
2 3
输出样例:
7
代码:
// 包含基础输入输出、算法头文件
#include <iostream>
#include <algorithm>
using namespace std;
// 定义长整型别名:防止乘法溢出(如多个质数相乘可能超过int范围)
typedef long long LL;
// 常量定义:N=20,最多支持20个质数(2^20≈1e6,循环次数可接受)
const int N = 20;
int p[N]; // 存储输入的m个质数
int main()
{
int n, m; // n:范围上限(1~n);m:质数的个数
cin >> n >> m;
// 输入m个质数,存入p数组
for (int i = 0; i < m; i ++ ) cin >> p[i];
int res = 0; // 最终结果:1~n中能被至少一个质数整除的数的个数
// ========== 核心:二进制枚举所有非空子集(容斥原理) ==========
// 1<<m 表示2^m(所有子集的数量),i从1开始枚举非空子集
for (int i = 1; i < 1 << m; i ++ )
{
int t = 1; // 存储当前子集所有质数的乘积
int s = 0; // 存储当前子集的元素个数(质数个数)
// 遍历当前子集的每一位,判断是否包含第j个质数
for (int j = 0; j < m; j ++ )
// i >> j & 1:判断i的第j位是否为1(包含第j个质数)
if (i >> j & 1)
{
// 防溢出:如果当前乘积*t超过n,后续n/t=0,无需计算
if ((LL)t * p[j] > n)
{
t = -1; // 标记溢出,跳出循环
break;
}
t *= p[j]; // 累乘当前质数
s ++ ; // 子集大小+1
}
// 如果乘积未溢出(有效子集)
if (t != -1)
{
// 容斥原理:奇加偶减
// 子集大小为奇数:加上n/t(1~n中能被t整除的数的个数)
// 子集大小为偶数:减去n/t
if (s % 2) res += n / t;
else res -= n / t;
}
}
// 输出结果
cout << res << endl;
return 0;
}

浙公网安备 33010602011771号