nyoj 998 点击这里打开题目链接
给你一个数N,使得在1~N之间能够找到x使得x满足gcd( x , N ) >= M,
求解gcd(x,N)的和
思路:一开始想到暴力法做,超时 ,后来借鉴学长经验AC:
大致思路: 用欧拉函数求 ,euler(n) 表示 1到n与n互质的数的个数, 如果n能够被 i 整除 ,
则euler(n/i)等价于gcd(x,N)==i 的个数 (x是从1到n的所有数字)。
所以,你会想到一个for循环,因为gcd(x,N)的值为1到n 枚举 gcd为M到n,再来求和? 时间复杂度也会很高,
所以并不需要全部一一枚举
比如gcd(x,N) N为 10 , 它们的gcd 只有 1,10 2,5
N为15 它们的gcd 有 1,15, 3,5
所以它们的gcd 只需要知道1 到 根号n 而另外一个gcd 为 n/i ;
for(i=1;i*i<=n;i++)
{
if(n%i==0)
{
if(i>=m) sum+=i*euler(n/i);
if(i*i!=n&&n/i>=m) sum+=(n/i)*euler(i);
}
}
注意: 第二个if i *i!=n 因为在第一个if里面已经加过了
下面贴个代码
#include <stdio.h>
#include <math.h>
long long euler(long long n)
{
long long i,sum=n,temp=n;
for(i=2;i*i<=temp;i++)
{
if(n%i==0)
{
sum=sum/i*(i-1);
while(n%i==0)
n/=i;
}
}
if(n>1)
sum=sum/n*(n-1);
return sum;
}
int main()
{
long long m,n;
while((scanf("%lld %lld",&n,&m))!=EOF)
{
long long sum=0,i;
for(i=1;i*i<=n;i++)
{
if(n%i==0)
{
if(i>=m) sum+=i*euler(n/i);
if(i*i!=n&&n/i>=m) sum+=(n/i)*euler(i);
}
}
printf("%lld\n",sum);
}
return 0;
}