Problem Description Given a number N, you are asked to count the number of integers between A and B inclusive which are relatively prime to N.
Input The first line on input contains T (0 < T <= 100) the number of test cases, each of the next T lines contains three integers A, B, N where (1 <= A <= B <= 1015) and (1 <=N <= 109).
Output For each test case, print the number of integers between A and B inclusive which are relatively prime to N. Follow the output format below.
Sample Input 2 1 10 2 3 15 5
Sample Output Case #1: 5 Case #2: 10 Hint In the first test case, the five integers in range [1,10] which are relatively prime to 2 are {1,3,5,7,9}.
Source |
这是个好题,觉得有必要记录一下,看了各个大佬的blog,觉得有一篇讲的比较好,看他的才明白的https://blog.csdn.net/wyg1997/article/details/51998119
首先我们想到的肯定是欧拉函,但是面对区间【1,n】与k互质的个数,欧拉函数必须满足k小于等于n,此题明显不行
换个思路,我们求【1,b】中与k互质的个数 减去 【1,a-1】中与k互质的个数
再转化为 我们求【1,b】中与k不互质的个数 和 【1,a-1】中与k不互质的个数
此时,大佬们先将k质因数分解,将所有的质因数因子放到数组p中,
在区间【1,n】中,与k不互质的数必定是k的质因子的倍数,因为与k不互质的数和k的最大公约数肯定不为一
但是这样枚举的话,有可能会重复算,比如区间中的一个数m,既是2的倍数,也是3的倍数,就会多减一次,此时就要加回来
很容易想到容斥原理,加奇数减偶数,这里的奇数偶数是同时满足奇数个集合或偶数个集合的意思
有关容斥原理:https://blog.csdn.net/qq_41431457/article/details/89949613
有了以上的思路,就需要枚举所有的素因子排列,假如有m个素因子,就有2^m种排列,于是大佬们就想到了用二进制
假如k有5个素因子, 面对其中的一种排列10001,就用到了p[1] 和 p[5] (从左往右),
第i位为 ‘1’ 就表示用到了第i个素因子,‘0’表示没有用到,
根据容斥原理,用到奇数个素因子就加上,偶数个就减去
【1,n】区间中的能整除p[1] 的数有 n / p [1]个,这些数肯定与k不互质,
所以枚举素因子排列就可以筛选掉所有与k不互质的数
#include<bits/stdc++.h>
#define lom long long
using namespace std;
int p[100000],num;
lom a,b,k;
void decomposition(int x)//分解x的质因子
{
num=0;
for(int i=2; i*i<=x; i++)
if(x%i==0)
{
p[num++]=i;
while(x%i==0) x/=i;
}
if(x>1) p[num++]=x;
}
lom slove(lom n)//求1~n中与k互质的个数
{
lom ans=0, lim=(lom)1<<num, cnt, temp;
for(lom i=1; i<lim; i++)
{
cnt=0, temp=1;
for(int j=0; j<num; j++)
if( ((lom)1<<j) & i)//表示第j个素因子被用到
{
temp*=p[j];//累乘
cnt++;
}
if(cnt&1) ans+=n/temp;//有n/temp个数 能整除temp
else ans-=n/temp;
}
return n-ans;
}
int main()
{
int t,cas=0;
cin>>t;
while(t--)
{
cin>>a>>b>>k;
decomposition(k);
printf("Case #%d: %lld\n",++cas,slove(b)-slove(a-1));
}
return 0;
}