• 基础数论--约数


    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 }
  • 相关阅读:
    个人编写的一个简单的DB类
    Access绝对地址
    sentry cli 上传source map
    自动旋转的饼图(echarts)
    点会转的折线图还带着柱子
    node 自动化工程部署
    SQL 模糊查询
    编写一个将输入复制到输出的程序,并将其中连续的多个空格用一个空格代替。
    configure、make、make install 解释
    Linux学习笔记(实时更新)
  • 原文地址:https://www.cnblogs.com/greenofyu/p/14081732.html
Copyright © 2020-2023  润新知