在博客NYOJ 998 中已经写过计算欧拉函数的三种方法,这里不再赘述。
本题也是对欧拉函数的应用的考查,不过考查了另外一个数论基本定理:如何用欧拉函数求小于n且与n互质所有的正整数的和。
记euler(x)公式能计算小于等于x的并且和x互质的数的个数;我们再看一下如何求小于等于n的和n互质的数的和, 我们用sum(n)表示;
定理:若gcd(x, a)=1,则有gcd(x, x-a)=1;
证明:反证法:假设gcd(x, x-a)=k (k>1),那么有(x-a)%k=0---1式,x%k=0---2式; 由1式和2式可得 a%k=0---3式; 由2式和3式可得gcd(x, a)=k,与gcd(x,a)=1矛盾,假设不成立,即原式得证;
由此我们可以得知小于x并且与x互质的数必然是成对出现的并且有对应的一对数和为x;所以有sum(n)=euler(n)/2*n;
故本题的写法为:
1 #include <iostream> 2 #include <cstdio> 3 using namespace std; 4 5 const int N=1000000007 ; 6 typedef long long LL; 7 8 LL Euler(LL n){ 9 LL ans = n; 10 for(int i = 2; i * i <= n; i++){ 11 if(n % i == 0){ 12 ans = ans / i * (i-1); 13 while(n % i == 0) 14 n /= i; 15 } 16 } 17 if(n > 1) ans = ans / n * (n-1); 18 return ans; 19 } 20 21 //计算得到小于n且与n互质的正整数的和 22 long long Euler_sum(long long n){ 23 if(n==1) 24 return 1; 25 else 26 return n*Euler(n)/2; 27 } 28 29 int main(){ 30 int t; 31 cin>>t; 32 while(t--){ 33 LL n,m; 34 cin>>n>>m; 35 LL ans = 0; 36 for(int i = 1; i * i <= n; i++){ 37 if(n % i == 0){ 38 if(i >= m){ 39 int d = i; 40 ans = (ans+d*Euler_sum(n/d) ) %N; 41 //考虑gcd(x,n) 1<=x<=n 42 //其中gcd(x/d,n/d) = 1 ,Euler(n/d)我们得到的是能够使得gcd(x,n) = d 的x的取值的个数 43 //Euler_sum(n/d)我们得到的是小于n/d且与n/d互质的正整数的和 44 //暂且设“小于n/d且与n/d互质的正整数”分别为a1,a2...an等那么乘以d之后得到的数列b1 = a1*d b2 = a2*d ...bn = an*d 45 //那么b1 b2 ...bn等数与n的公约数就是d ,而d>=m,满足题设 46 //所以在公约数为d的情况下这些能够满足gcd(x,n) = d >=m的数的和,即b1+b2+...bn = (a1+a2+...+an)*d, 47 //而(a1+a2+...+an) = Euler_sum(n/d) 故b1+b2+...bn = d*Euler_sum(n/d) 48 //这样就处理完了公约数为d的情况,同理按照for循环,1~sqrt(n)遍历, 49 //分别测试公约数为别等于i(i从1到sqrt(n)遍历)是否满足n%i==0即可,若满足就令d=i,ans+=d*Euler_sum(n/d) 50 } 51 if(i * i != n && n / i >= m){ 52 int d = n / i; 53 ans = (ans+ d*Euler_sum(n/d) )%N; 54 //在上部for循环中进行到sqrt(n),这一步就是处理后面的东西:n/i 55 } 56 } 57 } 58 cout<<ans<<endl; 59 } 60 return 0; 61 }