zoukankan      html  css  js  c++  java
  • 数论基础

    求素数(单个&线性筛)

    单个素数判断

    枚举看n是否能整除i。(O(sqrt{n}))

    bool Prime(int x) {
        if (x < 2) return 0;
        for (int i = 2; i * i <= x; ++i)
            if (x % i == 0) return 0;
        return 1;
    }
    

    线性筛

    保证每个和数只被最小的质因子筛掉。(O(n))

    int prime[N], tot;//记录素数
    bool v[N];//被标记说明不是素数
    void Primes(int n) {
        v[0] = v[1] = 1;//0和1不是素数
        for (int i = 2; i <= n; ++i) {
            if (!v[i]) prime[++tot] = i;//没有被标记过,是素数
            for (int j = 1; j <= tot && i * prime[j] <= n/*这里比限制n大的就不用筛了*/; ++j) {
                v[i*prime[j]] = 1;
                if (i % prime[j] == 0) break;//只被最小的质因子筛掉。
            }
        }
    }
    

    求欧拉函数(单个&线性筛)

    欧拉函数

    • 1~N 中与 N 互质的数的个数称为欧拉函数,记为 (varphi (N))
    • 考虑 (varphi (p^k) (pin prime))的取值是总数减去不互质的数的个数,不互质的数有如下 (p^{k-1})

    [1 imes p,2 imes p,cdots ,p^{k-1} imes p ]

    • 所以

    [varphi (p^k)=p^k-p^{k-1}=p^k(1-frac{1}{p}) ]

    • 再然后可以根据中国剩余定理证得欧拉函数是积性函数,所以

    [varphi (p^{c_1}p^{c_2}cdots p^{c_m})=varphi (p^{c_1})varphi (p^{c_m})cdots varphi (p^{c_m}) ]

    • 根据算术基本定理

    [N = p_ 1^ {c_ 1}p_ 2^{c_ 2}...p_ m^{c_m} ]

    [varphi (N)=N imes frac{p_1-1}{p_1} imes frac{p_2-1}{p_2} imes ... imesfrac{p_m-1}{p_m}=N imes prod_{质数p|N}(1-frac{1}{p}) ]

    单个求法

    • 根据欧拉函数的计算式,分解质因数即可进行求解。(O(sqrt{n}))
    int Phi(int n) {
        int ans = n;
        for (int i = 2; i * i <= n; ++i)
            if (n % i == 0) {
                ans = ans / i * (i - 1);//根据计算式计算
                while (n % i == 0) n /= i;
            }
        if (n > 1) ans = ans / n * (n - 1);
        //如果最后n>1,则为一个大于根号n的一个质因子
        return ans;
    }
    

    线性筛欧拉函数

    • 欧拉函数是积性函数,可以线性筛

    • 有式子

    [varphi (i*p)= left{egin{matrix} varphi (p) varphi (i),gcd(p,i)=1 \ p imes varphi (i),gcd(p,i) eq 1 end{matrix} ight.]

    • 由于积性函数,当 (p,i) 互质时显然成立
    • (p,i) 不互质,因为 (p) 是质数,(i) 一定有 (p) 这个因子,所以 (i)(i imes p) 一定有相同的质因子,只是在 (p) 这一项的指数不一样
    • 那么我们可以将其按照欧拉函数的计算式展开,并相除,可得:

    [frac{varphi (i imes p)}{varphi (i)}=frac{i imes p imes prod_{i=1}^m(1-frac{1}{p_i})}{i imes prod_{i=1}^{m}(1-frac{1}{p_i})}=frac{i imes p}{i}=p ]

    • 所以

    [varphi (i imes p)=varphi (i) imes p ]

    • 原式得证
    • 写出代码就很简单了
    void Euler(int n) {
        phi[1] = 1;
        for (int i = 2; i <= n; ++i) {
            if (!v[i]) prime[++tot] = i, phi[i] = i - 1;
            for (int j = 1; j <= tot && i * prime[j] <= n; ++j) {
                v[i*prime[j]] = 1;
                if (i % prime[j]) phi[i*prime[j]] = phi[i] * phi[prime[j]];
                else { phi[i*prime[j]] = phi[i] * prime[j]; break; }
            }
        }
    }
    

    扩展欧几里得算法

    • 用途:求 (ax+by=gcd(a, b)) 的特解
    • 证明:

    [bx'+amod bcdot y'=gcd(b,amod b) ]

    [bx'+(a-left lfloor frac{a}{b} ight floor)y'=gcd(b,amod b) ]

    [ay'+b(x'-left lfloor frac{a}{b} ight floor y')=gcd(b,amod b) ]

    所以得出

    [x=y',y=x'-left lfloor frac{a}{b} ight floor y' ]

    Code

    int Exgcd(int a, int b, int &x, int &y) {
        if (b == 0) return x = 1, y = 0, a;
        int d = Exgcd(b, a % b, x, y);
        int z = x; x = y; y = z - a / b * y;
        return d;
    }
    
    • 上述代码 x,y 是以引用的方式传递的,此函数求出方程 (ax+by=gcd(a, b)) 的一组特解并返回 a,b 的最大公约数。还有一种较为简便的写法,将上述最后一个式子的x'替换成y',y'替换成x'。得出以下结论:

    [x=x',y=y'-left lfloor frac{a}{b} ight floor x' ]

    int Exgcd(int a, int b, int &x, int &y) {
        if (!b) return x = 1, y = 0, a;
        int d = Exgcd(b, a % b, y, x);//这里x,y是反着的
        y -= a / b * x;
        return d;
    }
    

    乘法逆元

    • 定义:若整数 (a,m) 互质,并且 (b|a),则存在一个 (x),使得 (a/bequiv a cdot xpmod{m}),称 (x)(b) 的模 (m) 乘法逆元,记为 (b^{-1}pmod{m})
    • 求法:(b^{-1} = b^{varphi (p)-1})
    • 线性求法:
      • 推导:

    [ ext{设 } m=qcdot b+r ]

    [qcdot b+requiv 0pmod{m} ]

    [qcdot r^{-1}+b^{-1}equiv 0pmod{m} ]

    [b^{-1}equiv -qcdot r^{-1}pmod{m} ]

    [ ext{由 }q=m/b, r=mmod b ext{ 得} ]

    [b^{-1}equiv -m/bcdot (mmod b)^{-1}pmod{m} ]

    写出来就是inv[i] = (-M / i * inv[M%i] % M + M) % M;

        inv[1] = 1;
        for (int i = 2; i <= n; ++i)
            inv[i] = 1ll * (M - M / i) * inv[M%i] % M;
    

    排列、组合

    • 排列数:

    [A_n^m = frac{n!}{(n-m)!} ]

    • 组合数:

    [C_n^m=frac{n!}{m!(n-m)!} ]

    • 根据式子算就好了

    Code

    int Pow(int a, int k, int ans = 1) {
        for (; k; k >>= 1, a = 1LL * a * a % M)
            if (k & 1) ans = 1LL * ans * a % M;
        return ans;
    }
    
    void Pre(int n) {//fac是阶乘,inv是逆元,fi是阶乘的逆元
        fac[0] = 1;
        for (int i = 1; i <= n; ++i)
            fac[i] = 1LL * fac[i-1] * i % M;
        fai[n] = Pow(fac[n], M - 2);
        for (int i = n; i >= 1; --i)
            fai[i-1] = 1LL * fai[i] * i % M;
    }
    
    int A(int n, int m) {//排列
        return fac[n] * fai[n-m] % M;
    }
    int C(int n, int m) {//组合
        return fac[n] * fai[m] % M * fai[n-m] % M;
    }
    
  • 相关阅读:
    React教程(一) React介绍与搭建
    微信公众号订阅号以及服务号通过网页授权获取用户openid方法
    AES,DES加密JS源文件及其使用方法
    R 分类进行数值处理
    C++ const 关键字总结
    软工lintcode作业
    [恶意软件分析]DroidBox的环境搭建与使用
    Android第三次作业
    Android第二次作业
    android 第一次作业
  • 原文地址:https://www.cnblogs.com/shawk/p/13398100.html
Copyright © 2011-2022 走看看