题目连接
题意
输入a,b,n,要求区间[a,b]中与n互质的数的个数(1<=a<=b<=10^15 ; 1<=n<=10^9)。
思路
判断两个数是否互质,首先想到辗转相除法,然而这里的a,b相当大,枚举a,b间的每一个数并判断和n是否互质必然超时。
既然判断两个数互质方法单一,那么我们可以从反面考虑,首先计算出区间[a,b]里与n不互质的的数的个数sum,然后用( b - a + 1)- sum即可。接下来就来讲讲如何计算与区间内与n不互质的数的个数:
将n进行素因子分解,此处不需要记录每个素因子的指数,只需记录底数即可,如果x是n的某个素因子的倍数,那么x一定与n不互质(两数互质的充要条件是两数的最大公约数为1,因此1与任何数互质)。fac[]分别记录每个素因子, 那么所求区间内与某个质因数不互质的个数就是x/fac[i],假设只有三个质因子, 总的不互质的个数应该为p1+p2+p3-p1*p2-p1*p3-p2*p3+p1*p2*p3(容斥原理),当有更多个质因子的时候, 可以用状态压缩解决,二进制位上是1表示这个质因子被取进去了。 如果有奇数个1,就相加,反之则相减。
#include<iostream>
using namespace std;
typedef unsigned long long ll;
//fac[]仅保存n的素因子的底数,因为此处不需要指数,有时用vector
int fac[100000];
int cnt;//记录素因子个数
//分解素因子
void play_factor(ll n)
{
cnt=0;//初始化
for(ll i=2;i<=n/i;i++)
{
if(n%i==0)
{
fac[++cnt]=i;
while(n%i==0)
n/=i;
}
}
if(n>1)
fac[++cnt]=n;
}
//求解[1,x]中与n互质的数的个数
ll solve(ll x)
{
ll sum=0,val,k,i,j;
//容斥原理
for(i=1;i<(1<<cnt);i++)
{//状压
val=1;
k=0;//记录用了几个因子
//用二进制来1,0来表示第几个素因子是否被用到,如m=3,
//三个因子是2,3,5,则i=3时二进制是011,表示第2、3
//个因子被用到
for(j=0;j<cnt;j++)
{
if(i&(1<<j))//判断目前几个因子被用到
{
val*=fac[j+1];
k++;
}
}
if(k&1)//奇加偶减
sum+=x/val;
else
sum-=x/val;
}
return x-sum;
}
int main()
{
int T,cas=1;
ll n,a,b;
cin>>T;
while(T--)
{
cin>>a>>b>>n;
play_factor(n);
ll ans=solve(b)-solve(a-1);
cout<<"Case #"<<cas++<<": "<<ans<<endl;
}
return 0;
}