NYOJ 1007

在博客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 }

 

posted on 2017-01-07 19:25  逸阳  阅读(149)  评论(0编辑  收藏  举报

导航