AcWing 第四讲 数学知识

  • Post author:
  • Post category:其他




1. 质数


在 > 1的整数中, 如果只包含1和本身这两个约数,就是质数,也叫素数

n <= 1不是质数



质数的判定-试除法


质数的约数都是成对出现的,可以枚举每一对中较小的那个

// O(sqrt(n))
bool is_prime(int n){
   if(n <= 1) return false;
   for(int i = 2; i <= n / i; i++) // i * i <= n 可能会越界 
       if(n % i == 0)
           return false;
   return true;
}


为什么是 i <= n/i


稍微好一点,临界情况 i*i会爆掉

在这里插入图片描述



分解质因数-试除法


AcWing 867. 分解质因数


在这里插入图片描述

O(n)
//分解质因数
void divide(int n){
    for(int i = 2; i <= n; i++){
        if(n % i == 0){  // [2~i-1]的因子都除干净了, i 一定是质数
            int s = 0;
            while(n % i == 0){
                s++;
                n /= i;
            }
            cout << i << " " << s << endl;  
        }

    }
    cout << endl;
}
// O(sqrt(n))
//只有一个 > sqrt(n) 的质因子
void divide(int n){
    for(int i = 2; i <= n/i; i++){
        if(n % i == 0){  
            int s = 0;
            while(n % i == 0){
                s++;
                n /= i;
            }
            cout << i << " " << s << endl;  
        }
    }
    if(n > 1) cout << n << " 1" << endl; // > sqrt(n)的质因子
    cout << endl;
}



AcWing 868. 筛质数


AcWing 868. 筛质数


在这里插入图片描述


朴素做法

 int prime[N],cnt;  /prime存放质数的值,cnt计数,下标
bool st[N];
/O(nlog(n))
void get_prime(int n){
    for(int i = 2; i <= n; i++){
        if(!st[i]) prime[cnt++] = i;
        for(int j = i; j <= n; j = j + i) st[j] = true;
    }
}

在这里插入图片描述


如果 2~p-1中,枚举的不是质数,那么该数不可能是 p 的约数!!

1~n中有 n/ln(n) 个质数 = nlogn个数,少算logn倍

所以比朴素筛法快了 log(n)倍


埃式筛法

/ O(nlog(log(n)) = O(n)
void get_prime(int n){
    for(int i = 2; i <= n; i++){
        if(!st[i]){
             prime[cnt++] = i;
             /优化,只删除质数的倍数
             for(int j = i; j <= n; j = j + i) st[j] = true;
        }
    }
}



线性筛法



线性筛法,在 n = 1e6 和 埃式筛法差不多

在 n = 1e7的时候,比埃式筛法快一倍

// O(n)
void get_prime(int n){
    for(int i = 2; i <= n; i++){ 
        if(!st[i]) primes[cnt++] = i;
        for(int j = 0; primes[j] <= n / i; j ++){
        	// primes[j] 筛掉 primes[j]*i = x 
            st[primes[j]*i] = true;
            if(i % primes[j] == 0) break; // primes[j] 是 i 的最小质因子
        }
    }
}


假设

i 是合数 t 的最大因数

,t 显然可能不唯一(例如 30 和 45 最大因数都是 15。但是仔细想一想,必然有


t = i * p

(

p为小于 i 的质数

) (i是t的最大因数)


p为什么比 i 小

因为 i 是 t 的最大因数。


为什么 p 一定是质数?

因为如果 p 是合数,那么 i 就一定不是 t 的最大因数,因为 p可以再拆成若干素数相乘,这些素数再与 i 相乘会使因数更大。

既然如此,我们只需要把所有小于 i 的质数 p 都挨个乘一次好了。可是,真相真的是这样的嘛?

其实不是的,一不小心就忘记了最初的条件。我们要满足 i 是 t 的最大因数。如果 p 大于 i 的最小质因数,那 i 还是 t 的最大因数嘛?显然不是,任何一个合数都能唯一分解为有限个质数的乘积,除去这其中最小的质因数,其他的都乘起来就是最大因数 i 。所以我们不能让 p 大于 i 的最小质因数。


n只会被最小质因子筛掉

 st[primes[j]*i] = true;


i % pj == 0 , pj 一定是i的最小质因子(primes[]数组存放的是所有的质数),pj 一定是 pj*i 的最小质因子



i% pj != 0 , pj一定小于i的所有质因子, pj 也一定是 pj*i的最小质因子


如何保证所有的合数都被删掉?



任何一个数都存在最小质因子



对于一个合数x,假设pj是x的最小质因子,当i 枚举到 x/pj的时候,x被筛掉

i 枚举到x之前肯定会被筛掉

void get_prime(int n){ // 线性筛法
    for(int i = 2; i <= n; i++){
        if(!st[i]) primes[cnt++] = i;
        //保证primes[j] 是 primes[j]*i 的最小质因数
        // 并且 x = primes[j]*i 是 被最小质因数筛掉的
        // 并且每个合数x都有最小质因数,都会被筛掉
        for(int j = 0; primes[j] <= n/i; j++){ 
            st[primes[j]*i] = true;
            if(i % primes[j] == 0) break;
        }
    }
}



2. 约数



1. 试除法求约数

int a; cin >> a;
vector<int> vec;
for(int i = 1; i <= a/i; i++){
    if(a % i == 0){
        vec.push_back(i);
        if(i != a/i) vec.push_back(a/i); // 16 = 4*4,只放一次4
    }
}



AcWing 870. 约数个数




N

=

p

1

α

1

p

2

α

2

p

3

α

3

.

.

.

p

k

α

k

N=p_1^{\alpha_1}*p_2^{\alpha_2} *p_3^{\alpha_3}*…*p_k^{\alpha_k}






N




=









p










1










α










1















































p










2










α










2















































p










3










α










3














































.


.


.














p










k










α










k








































a

n

s

=

(

α

1

+

1

)

(

α

2

+

1

)

.

.

.

(

α

k

+

1

)

ans=(\alpha_1+1)(\alpha_2+1)…(\alpha_k+1)






a


n


s




=








(



α










1




















+








1


)


(



α










2




















+








1


)


.


.


.


(



α










k




















+








1


)




因为N的任何一个约数d可以表示成



d

=

p

1

β

1

p

2

β

2

.

.

.

p

k

β

k

,

(

0

<

=

β

i

<

=

α

i

)

d=p_1^{\beta_1}*p_2^{\beta_2}*…p_k^{\beta_k}, (0<=\beta_i<=\alpha_i)






d




=









p










1










β










1















































p










2










β










2














































.


.


.



p










k










β










k



































,




(


0




<






=









β










i




















<






=









α










i


















)




每一项的



β

i

\beta_i







β










i





















如果不同,那么约数d就不同

n的约数个数是与



β

i

\beta_i







β










i





















的选法是一一对应的




β

1

0

α

1

\beta_1一共有0-\alpha_1中选法







β










1



























0














α










1


































β

2

0

α

2

\beta_2一共有0-\alpha_2中选法







β










2



























0














α










2




































β

k

0

α

k

\beta_k一共有0-\alpha_k中选法







β










k



























0














α










k































d 有



(

α

1

+

1

)

(

α

2

+

1

)

.

.

.

(

α

k

+

1

)

(\alpha_1+1)*(\alpha_2+1)*…*(\alpha_k+1)






(



α










1




















+








1


)













(



α










2




















+








1


)













.


.


.













(



α










k




















+








1


)





中选法

选法原理

#include <iostream>
#include <unordered_map>
using namespace std;
unordered_map<int,int> mp;
const int MOD = 1e9 + 7;
int main(){
    ios::sync_with_stdio(false); cin.tie(0);

    int T; cin >> T; while(T--){
        int n; cin >> n;
        for(int i = 2; i <= n/i; i++){ //寻找质因数,只有一个 > sqrt(n)的质因子
            while(n % i == 0){
                mp[i]++;
                n /= i;
            }
        }
        if(n > 1) mp[n]++;

    }
    long long ans = 1;
    for(auto p : mp){
        ans = ans * (p.second + 1) % MOD;
    }
    cout << ans << endl;
    return 0;
}



AcWing871. 约数之和




N

=

p

1

α

1

p

2

α

2

p

3

α

3

.

.

.

p

k

α

k

N=p_1^{\alpha_1}*p_2^{\alpha_2} *p_3^{\alpha_3}*…*p_k^{\alpha_k}






N




=









p










1










α










1















































p










2










α










2















































p










3










α










3














































.


.


.














p










k










α










k










































a

n

s

=

(

p

1

0

+

p

1

1

+

.

.

.

+

p

1

α

1

)

.

.

.

(

p

k

0

+

p

k

1

+

.

.

.

+

p

k

α

k

)

ans=(p_1^0+p_1^1+…+p_1^{\alpha_1})*…*(p_k^0+p_k^1+…+p_k^{\alpha_k})






a


n


s




=








(



p










1








0




















+









p










1








1




















+








.


.


.




+









p










1










α










1



































)













.


.


.













(



p










k








0




















+









p










k








1




















+








.


.


.




+









p










k










α










k



































)




将ans展开后,每一项都是一个约数 d




a

n

s

=

d

1

d

2

.

.

.

d

k

ans=d_1*d_2*…*d_k






a


n


s




=









d










1






























d










2





























.


.


.














d










k

























d

i

=

p

1

β

1

p

2

β

2

.

.

.

p

k

β

k

,

(

0

<

=

β

i

<

=

α

i

)

d_i=p_1^{\beta_1}*p_2^{\beta_2}*…p_k^{\beta_k}, (0<=\beta_i<=\alpha_i)







d










i




















=









p










1










β










1















































p










2










β










2














































.


.


.



p










k










β










k



































,




(


0




<






=









β










i




















<






=









α










i


















)




#include <iostream>
#include <unordered_map>
using namespace std;
unordered_map<int,int> mp;
const int MOD = 1e9 + 7;
int main(){
    ios::sync_with_stdio(false); cin.tie(0);

    int T; cin >> T; while(T--){
        int n; cin >> n;
        for(int i = 2; i <= n/i; i++){ //寻找质因数,只有一个 > sqrt(n)的质因子
            while(n % i == 0){
                mp[i]++;
                n /= i;
            }
        }
        if(n > 1) mp[n]++;

    }
    long long ans = 1;
    for(auto p : mp){
        int fi = p.first, se = p.second;
        long long t = 1;
        while(se--) t = (t * fi + 1) % MOD;
        ans = ans * t % MOD;
    }
    cout << ans << endl;
    return 0;
}



AcWing 872. 最大公约数


AcWing 872. 最大公约数


如果d整除a,b




d

/

a

d/a






d


/


a









d

/

b

d/b






d


/


b






那么d可以整除



d

/

(

a

x

+

b

y

)

d/(ax+by)






d


/


(


a


x




+








b


y


)









a

%

b

=

a

[

a

/

b

]

b

=

a

c

b

a\%b=a-[a/b]*b=a-c*b






a


%


b




=








a













[


a


/


b


]













b




=








a













c













b









a

%

b

=

b

%

(

a

c

d

)

a\%b=b\%(a-c*d)






a


%


b




=








b


%


(


a













c













d


)









a

%

b

=

b

%

(

a

%

b

)

a\%b=b\%(a\%b)






a


%


b




=








b


%


(


a


%


b


)






左边的公约数,等于右边的公约数

int gcd(int a,int b){
   return b?gcd(b,a%b):a;
}



版权声明:本文为m0_37642480原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。