HDU 4135 Co-prime(容斥:二进制解法)题解

时间:2023-12-04 09:33:26

题意:给出[a,b]区间内与n互质的个数

思路:如果n比较小,我们可以用欧拉函数解决,但是n有1e9。要求区间内互质,我们可以先求前缀内互质个数,即[1,b]内与n互质,求互质,可以转化为求不互质,也就是有除1的公因数。那么我们把n质因数分解,就能算出含某些公因数的不互质的个数。因为会重复,所以容斥解决。因为因数个数可能很多(随便算了一个20!> 2e18,所以质因数分解个数不会超过20个),我们可以用二进制来遍历解决。

#include<set>
#include<map>
#include<cmath>
#include<queue>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;
const int maxn = + ;
const int seed = ;
const int MOD = + ;
const int INF = 0x3f3f3f3f;
ll y[], cnt;
ll a, b, n;
ll solve(ll num){
ll sum = , val, tot;
for(ll i = ; i < ( << cnt); i++){ //这里是小于最多只能cnt位
val = ;
tot = ;
for(ll j = ; j < cnt; j++){
if(i & ( << j)){ //第j个因子被用到
val *= y[j];
tot++;
}
}
if(tot & ){
sum += num / val;
}
else{
sum -= num / val;
}
}
return num - sum;
}
int main(){
int T, Case = ;
scanf("%d", &T);
while(T--){
scanf("%lld%lld%lld", &a, &b, &n);
cnt = ;
ll x = n;
for(ll i = ; i * i <= x; i++){
if(x % i == ){
y[cnt++] = i;
while(x % i == ){
x /= i;
}
}
}
if(x > ){
y[cnt++] = x;
}
printf("Case #%d: %lld\n", Case++, solve(b) - solve(a - ));
}
return ;
}