hdu 2588 GCD【欧拉+gcd推导*经典 详解】

题意:输入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);
  }
}

猜你喜欢

转载自blog.csdn.net/qq_41668093/article/details/83752101