题意:输入n m 表示从1到n的数与n的公约数大于m的数的个数
这道题所需要的算法主要为欧拉函数的运用和一点点的GCD知识。
问题所要求的是 gcd( x , n ) > =m ,由gcd( x , n )本身可知,gcd求出来的是 x 和n的最大公约数(设为a),即有式子gcd( x ,n )=a , 进一步进行化简可变为gcd( x/a , n/a )=1 , 到了此处这个式子又有了另一层含义——x/a与n/a互素(互质) 。再联想到欧拉函数的功能——对正整数n,欧拉函数是小于或等于n的数中与n互质的数的数目。于是将欧拉函数里的n换成n/a,不就正好能求出x/a的个数了吗?x/a的个数不就是我们所要求的x的个数了吗?(●'◡'●) 转自
另一个同类解释:
①我们先看两个数 N = a*b,X= a*d。因为gcd ( N , X ) = a 所以b,d这两个数互质。又因为d可以是任何一个小于b的数。所以d值数量的的多少就是b的欧拉函数值。所以,我们可以枚举a,然后去求b,然后再求b的欧拉函数值。
②但是如果单纯这样全部枚举的话依旧会超时,所以我们要想一个办法去优化它。我们可以折半枚举,这里的折半并不是二分的意思。
我们先看,我们枚举时,当i<sqrt(n),假设a=n / i, 当i>sqrt(n)之后 有b=n/i,我们观察到当n%i==0时,会出现一种情况,就是a*b==n。所以我们就可以只需要枚举sqrt(n)种情况,然后和它对应的情况就是 n/i。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll n,m;
ll euler(ll x)
{
ll ans=x;//最终答案
for(ll i=2; i*i<=x; i++)
{
if(x%i==0)找到a的质因数
{
ans=ans/i*(i-1);//先进行除法是为了防止中间数据的溢出
while(x%i==0) x/=i;//x通过质因子分解 x/=i 质因数
}
}
if(x>1) ans=ans/x*(x-1);
return ans;
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
scanf("%lld %lld",&n,&m);
ll ans=0;
for(ll i=1; i*i<=n; i++) //判断sqrt (n)就好
if(n%i == 0)
{
if(i >= m) ans += euler(n/i);//求小于或等于 n/i 的数中与 n/i 互质的数的数目
//i*i只能加一次
if(n/i >= m && i*i != n) ans += euler(i);//当i=nn时,n/i=n,要避免求两次i的欧拉函数
}
printf("%lld\n",ans);
}
}