基础数论--约数

1、试除法求约数

朴素想法是直接枚举1-n,如果n%i==0,i就是约数

但是考虑到约数是成对出现的,假设i是n的约数,那么n/i也是n的约数,所以只需要枚举到sqrt(n)即可

时间复杂度为:sqrt(n),需要证明排序的复杂度小于求约数的复杂度

              排序复杂度是nlogn,那么我们就得知道n有多少个约数,但是对于不同的数的约数个数是不同的,所以我们只能估计

           我们先求出1---n中约数的个数,然后除以n就是平均每个数的约数的个数

           然后因为a是b的约数,那么b就是a的倍数,所以1---n中约数的个数等于倍数的个数

           而1的倍数有n个,2的倍数有n/2个.....因此1---n中约数的个数为n(1+1/2+1/3+1/4+....)=nln n

           所以平均每个数有ln n约等于log n个倍数。

           排序复杂度为logn*log log n ,小于sqrt(n)

 1 void get_divisors(int n){
 2     res.clear();
 3     for(int i=1;i<=n/i;i++){
 4         if(n%i==0){
 5             res.push_back(i);
 6             if(n/i>i){
 7                 res.push_back(n/i);
 8             }
 9         }
10     }
11     sort(res.begin(),res.end());
12 }

2、约数个数和约数之和

  这俩是基于唯一分解定理来的

  每一个数大于一的数 x 都可以被分解为 x=p1^a1+p2^a2+....pk^ak的形式

  所以对于一个数n,他的约数个数就为(a1+1)(a2+1)....(ak+1)

          他的约数之和就为(p1^0+p1^1+...+p1^a1)(p2^0+p2^1+...+p2^a2)....(pk^0+pk^1+...+pk^ak)

 

  约数个数(给定n个数,求这些数的乘积的约数个数)

 1 #include<iostream>
 2 #include<unordered_map>
 3 using namespace std;
 4 const int mod=1e9+7;
 5     
 6 int main(void){
 7     int n;
 8     unordered_map<int,int> mp;
 9     cin>>n;
10     for(int i=0;i<n;i++){
11         int a;
12         cin>>a;
13         for(int j=2;j<=a/j;j++){
14             while(a%j==0){
15                 mp[j]++;
16                 a/=j;
17             }
18         }
19         if(a>1){
20             mp[a]++;
21         }
22     }
23     long long res=1;
24     for(auto it:mp){
25         res=res*(it.second+1);
26         res%=mod;
27     }
28     cout<<res;
29     return 0;
30 }

 

  约数之和

 1 #include<iostream>
 2 #include<unordered_map>
 3 using namespace std;
 4 const int mod=1e9+7;
 5 int main(void){
 6     int n;
 7     unordered_map<int,int> mp;
 8     cin>>n;
 9     for(int i=0;i<n;i++){
10         int a;
11         cin>>a;
12         for(int j=2;j<=a/j;j++){
13             while(a%j==0){
14                 a/=j;
15                 mp[j]++;
16             }
17         }
18         if(a>1){
19             mp[a]++;   
20         }
21     }
22 
23     long long res=1;
24     for(auto it:mp){
25         long long  t=1,prime=it.first,cnt=it.second;
26         while(cnt--){
27             t=t*prime+1;
28             t%=mod;
29         }
30         res=res*t;
31         res%=mod;
32     }
33     cout<<res;
34     return 0;
35 }

3、最大公约数

辗转相除法,gcd(a,b)=gcd(b,a%b)

(规定0可以整除任何数)

证明:数学基本性质d|a,d|b---->d|xa+by,可由唯一分解定理证得

  左--->右:

   假设d|a,d|b,因为a%b=a-c*b,所以d|a-c*b,所以如果是a和b的约数,也一定是b和a%b的约数

  右--->左:

    假设d|b,d|a%b,所以d|a-c*b,又因为d|b,所以d|a-c*b+c*b--->d|a,所以d|a&&d|b

正确性得证。

 1 #include<iostream>
 2 using namespace std;
 3 int gcd(int a,int b){
 4     return b?gcd(b,a%b):a;
 5 }
 6 int main(void){
 7     int n;
 8     cin>>n;
 9     for(int i=0;i<n;i++){
10         int a,b;
11         cin>>a>>b;
12         cout<<gcd(a,b)<<endl;
13     }
14     return 0;
15 }

 

posted on 2020-12-03 17:56  greenofyu  阅读(358)  评论(0编辑  收藏  举报