Files
python/TangDou/AcWing/Math/RongChi/HDU4135.md
HuangHai 1f397eca87 'commit'
2025-08-30 18:35:01 +08:00

85 lines
2.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

[题目链接](http://acm.hdu.edu.cn/showproblem.php?pid=4135)
https://blog.csdn.net/qq_40507857/article/details/82788443
> 题意:求区间`[a,b]`中与`m`互素的数字个数
### 一、题目分析
考虑`[1,a-1]`和`[1,b]`两个区间与`m`互素的个数,答案就是二者之差(**类似前缀和?**)。
<font color='red'><b>容斥原理经典问题</b></font>:求`1-n`中与`m`互素的数的个数
互素的个数等于总数减去不互素的个数,如果$1-n$中某个数与$m$不互素,那么一定可以被$m$的某个因子整除,所以先枚举$m$的所有素因子。
举个例子,$m=12,n=8$,$12$的素因子是$2,3$,
$1\sim 8$中有几个是$2$的倍数呢?$S_2=8/2=4$;
$1\sim 8$中有几个是$3$的倍数呢?$S_3=8/3=2$;
$1\sim 8$之间与$12$互素的个数是$2+4=6$个数字吗?
$1\sim 8$之间与$12$不互素的是$2,3,4,6,8$,共$5$个数字,这是因为同为$2$和$3$的倍数的$6$被计算了两次,所以要再减去一次$S_6=8/6=1$,结果是 $$\large 8/2+8/3-8/(2*3)=4+2-1=5$$
这是经典的容斥原理啊,如果是奇数个组合,那么符号是`+`;如果是偶数个组合,那么符号是`-`;
### 二、实现代码
```c++
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
//返回1-m中与n互素的数的个数
vector<LL> p;
LL cal(LL n, LL m) {
p.clear();
for (int i = 2; i * i <= n; i++) {
if (n % i == 0) {
p.push_back(i);
while (n % i == 0) n /= i;
}
}
if (n > 1) p.push_back(n); //求n的素因子
int num = p.size(); //素因子的个数
LL s = 0; // 1到m中与n不互素的数的个数
//枚举子集,不能有空集,所以从1开始
for (LL i = 1; i < 1 << num; i++) { //从1枚举到(2^素因子个数)
LL cnt = 0;
LL t = 1;
for (LL j = 0; j < num; j++) { //枚举每个素因子
if (i & (1 << j)) { //有第i个因子
cnt++; //计数
t *= p[j]; //乘上这个质因子
}
}
//容斥原理
if (cnt & 1) //选取个数为奇数,加
s += m / t;
else //选取个数为偶数,减
s -= m / t;
}
return m - s; //返回1-m中与n互素的数的个数
}
int main() {
//加快读入
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int T, ca = 0;
cin >> T;
while (T--) {
LL m, a, b;
cin >> a >> b >> m; //求区间[a,b]中与m互素的数字个数
//计算[1,a-1]之间与m互素的个数
//计算[1, b]之间与m互素的个数
LL ans = cal(m, b) - cal(m, a - 1);
printf("Case #%d: %lld\n", ++ca, ans);
}
return 0;
}
```