基础数论

数论

数论

约数个数

  • 对于求解可以使用质因数分解的方式

    \(f(N)\) = \((C_1 + 1) (C_2 + 1)\)……\((C_k + 1)\)

    \(N\) = \(P_1^{C_1}\)\(P_2^{C_2}\)……\(P_k^{C_k}\)

时间复杂度

\(1\) ~ \(N\) 中,\(\sum\limits_{i=1}^N = Nlog(N)\)

int 范围内,\(MAXf(N)\) = 1600

求约数的方法详见[例题]([P1072 NOIP2009 提高组] Hankson 的趣味题 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn))

code

#include <bits/stdc++.h>
#define int long long 
using namespace std ;
const int maxn = 5e4 + 10 ;
int m , n , ans ;
struct node//存每个质因子
{
    int p , c ;//质因子,质因子的次数
}factor[1605] ;//int 范围内最多的约数的个数
int tot , cnt , num ; // 每个质因子的个数 , 约数的个数
int prime[maxn >> 1] , di[maxn] ;
bool is_prime[maxn] ;
void erlu(int x)
{
    for(int i = 2 ; i <= x ; i++)
    {
        if(! is_prime[i])   prime[++num] = i ;
        for(int j = 1 ; prime[j] <= x / i ; j++)
        {
            is_prime[prime[j] * i] = true ;
            if(! (i % prime[j]))  break ;
        }
    }
}
int gcd(int a, int b)
{
    return b ? gcd(b, a % b) : a;
}
void dfs(int u ,int p)
{
    if(u > tot)
    {
        di[++cnt] = p ;
        return ;
    }
    for(int  i = 1 ; i <= factor[u].c + 1 ; i++)
    {
        dfs(u + 1 ,p) ;
        p *= factor[u].p ;
    }
}
signed main()
{
    int a, b , c , d ;
    cin >> n ;
    erlu(maxn - 1) ;
    while(n--)
    {
        cin >> a >> b >> c >> d ;//b1,c是d的约数
        int t = d ; tot = 0 ;
        for(int i = 1 ; prime[i] <= t / prime[i] ; i++ )
        {
            int p = prime[i] ;
            if(! (t % p))//是d的质因子
            {
                int c = 0 ;
                while(! (t % p))  t /= p , c++ ;
                factor[++tot] = {p , c} ;
            }
        }//试除法的原理实现
        if(t > 1)//循环除的情况下,说明t也是d的质因子
            factor[++tot] = {t , 1} ;
        cnt = 0 , ans = 0 ;   
        dfs(1 , 1) ;//爆搜处理处所有的约数
        //cout <<cnt<<" " << num<<" " << tot<<" " ;
        for(int i = 1 ; i <= cnt ; i++ ) 
        {
            int x = di[i] ;
            if(gcd(a , x) == b && c * x / gcd(x , c) == d)  ans++ ;
        }
        cout << ans ;
        puts("") ;
    }
    return 0 ;
}

线性筛

法求欧拉函数

如果 \(p_j\)\(i\) 的最小质因子

\[φ(p_j \times i ) = φ(i) \times prime_j \]

如果 \(p_j\) 不是 \(i\) 的最小质因子

\[φ(p_j \times i ) = φ(i) \times (prime_j - 1) \]

如果 \(i\) 是质数

\[φ(i) = i - 1 \]

code

void erlu(int n)
{
    phi[1] = 1 ; 
    for(int i = 2 ; i <= n ; i++)
    {
        if(! is_prime[i])
        {
            phi[i] = i - 1 ;
            prime[++cnt] = i ;
        }
        for(int j = 1 ; prime[j] <= n / i ; j++ )
        {
            is_prime[prime[j] * i] = true ;
            if( i % prime[j] == 0 )
            {
                phi[prime[j] * i] = phi[i] * prime[j] ;
                break ;
            }
            phi[prime[j] * i] = phi[i] * ( prime[j] - 1 ) ;
         }
    }
}

以上性质在代码中均有体现

欧拉函数的其他性质

  1. 通式

    \[φ(n) = n \times (1 - \dfrac{1}{p_1})\times (1 - \dfrac{1}{p_2})……(1 - \dfrac{1}{p_k}) \]

    \(p_i\)是根据唯一分解定理分解出来的 \(n\) 的质因子

  2. \(a\) , \(b\) 互质

    \[φ(ab) = φ(a) \times φ(b) \]

  3. \(p\) 为质数

    \[φ(p^n) = p^n - p^{n-1} \]

欧拉定理和费马小定理

欧拉定理

若正整数 \(a\)\(n\) 互质,则 $a^{φ(n)} \equiv 1 (\mod n\ ) $

$a\times a_1 % n $ 是1~n中的一个质数

证明:

若 $a\times a_1 % n $ 不是1~n中的一个质数,那么$a\times a_1 % n $ = $ kn + b $ , 其中\(b\)\(n\)的一个因子,那么 \(a\times a_1\)\(kn + b\)\(n\) 有一个因子 b 和 a 。

费马小定理

欧拉定理的条件下,若 \(n\) 是一个质数,那么

\[ a^{p-1} \equiv 1 (\mod p\ ) \]

证明:

用欧拉函数的引理把 \(φ\) 替换掉就行了 。

应用:
费马小定理可以用来求乘法逆元
逆元:
\(a⋅x≡1(\mod b\ \ )\)
则称 \(x\)\(a\)mod \(b\) 意义下的逆元
[例题][1]
题意,求 \(a_i\)mod \(p_i\) 意义下的最小逆元。

\[ a_i⋅x\equiv 1(\mod p_i \ ) \]

首先利用费马小定理,构造处一组解

\[a_i ^ {p_i - 1} \equiv 1(\mod p_i \ ) \ \ \ \ \ \ \ \Rightarrow\ \ \ \ \ \ \ a_i ⋅ a_i^{p_i-2}\equiv1(\mod p_i \ ) \]

这里的 $ a_i^{p_i-2}$ 为 \(a_i\)mod \(p_i\) 意义下的最小逆元 , 即为所求 \(x\)
这里使用快速幂求解 $ a_i^{p_i-2}$ ,模数即为 \(p_i\)
最后考虑无解的情况,根据费马小定理的使用条件,要保证 \(a_i\)\(p_i\) 互质。所以对于 \(a_i\) % \(p_i\) = \(0\) 的情况直接特判掉,输出 \(“impossible”\)

高斯消元法

[定义][2]
原谅我实在太懒了~

具体实现方法

注:下面提到的所有操作都是对增广矩阵的操作
枚举每一列 \(c\)

  1. 找到绝对值最大的一行
  2. 将该行换到最上面
  3. 将该行的 \(c\) 列系数变成 \(1\)
  4. 将下面所有的第 \(c\) 列的系数消成 \(0\)

[模板题][3]

高斯消元法的基本实现步骤,代码中详细体现了上面列出的四步。注意本题数据加强后,会出现 \(-0.00\) 的情况注意特判掉。

code

#include <bits/stdc++.h>
using namespace std ;
const int maxn = 1e2 + 10 ;
const double eps = 1e-6 ;
int n ;
double a[maxn][maxn] ;
int gauss()
{
    int c , r ;// 列,行
    for(c = 0 , r = 0 ; c < n ; c++ )
    {
        int t = r;//r当前的“第一”行(意义不同
        for(int i = r ; i < n ; i++ )
            if(fabs(a[i][c]) > fabs(a[t][c]))
                t = i ; //目前最优的行数-> 1
        if(fabs(a[t][c]) < eps)   continue ;
        for(int i = c ; i <= n ; i++ )
            swap(a[t][i] , a[r][i]) ;
        //把每一列都放到最上面去-> 2
        for(int i = n ; i >= c ; i-- )
            a[r][i] /= a[r][c] ;
        //把这一行都除以第一个数-> 3
        for(int i = r + 1 ; i < n ; i++ )
        {
            if(fabs(a[i][c]) <= eps)    continue ;
            for(int j = n ; j >= c ; j-- )
                a[i][j] -= a[r][j] * a[i][c];
        //记住现在r行c列的系数已经是一了-> 4
        }
        r++ ;//下一行
    }
    if(r < n)
    {
        for(int i = r ; i < n ; i++ )
            if(fabs(a[i][n]) > eps)
                return 2 ;
        return 1 ;
    }
    for(int i = n - 1 ; i >= 0 ;i-- )
        for(int j = i + 1 ; j < n ; j++ )
            a[i][n] -= a[i][j] * a[j][n] ;
    //最后一步将等式右边减去x_j * 其系数=x_i
    //因为此时第i行的第i列系数已经化成1
    return 0 ;
}
signed main()
{
    cin >> n ; 
    for(int i = 0 ; i < n ; i++ )
        for(int j = 0 ; j < n + 1 ; j++ )
            cin >> a[i][j] ;
    int t = gauss() ;
    if(t == 2 )
        printf("No solution") ;
    else if(t == 1 )
        printf("Infinite group solutions") ;
    else 
    {
        for(int i = 0 ; i < n ; i++ )
        {
            if(fabs(a[i][n]) <= eps)  puts("0.00") ;
            else printf("%.2lf\n" , a[i][n]) ;
        }
    }
    return 0 ;
}

线性代数的知识自我感觉很抽象,建议初学者手玩下每步过程,加深印象。
[1]: https://www.acwing.com/problem/content/878/
[2]: https://baike.baidu.com/item/高斯消元法/619561
[3]: https://www.acwing.com/problem/content/885/

posted @ 2022-02-11 19:29  Simon_...sun  阅读(56)  评论(0)    收藏  举报