• 【瞎口胡】基础数学 1 快速幂 整除 最大公约数


    写在前面

    真的没啥可看的,就当我是给一万年后我校学 OI 的小朋友们写的(如果那时候还有的话)。

    普通选手还是别看了,浪费时间,而且体现了我的菜。

    符号说明

    以下符号都是文中不会解释的。

    (sum):求和符号,(sum _{i=1}^{n} d_i = d_1 + d_2 + ... + d_n)(sum_{d|n} d=) (n) 的正约数和。

    (prod):连乘积符号,(prod_{i=1}^{n} i = 1 imes 2 imes ... imes n)

    ( ext{mod}):模。(x mod y) 即为 (x) 除以 (y) 的余数,(2 mod 3 = 2,8 mod 4 = 0,5 mod 3 = 2)

    (min{},max{}):最小值,最大值。这都不会还来看这篇文章?

    快速幂

    快速幂利用了整数乘法的结合律。(a^{b+c} = a^b imes a^c),利用这个思想把 (a^c mod p)(O(log c)) 的时间内求出。

    具体的思想是把 (c) 二进制拆分成 (2^{r_1}+2^{r_2}+2^{r_3}+...2^{r_n}),其中 (r_i) 两两不同。再计算所有 (a^{2^{r_1}}) 的乘积,就得到了 (a^c)

    模板题 Luogu P1226 Code

    # include <bits/stdc++.h>
    # define int long long 
    
    const int N=100010,INF=0x3f3f3f3f;
    
    int b,p,k;
    
    inline int read(void){
    	int res,f=1;
    	char c;
    	while((c=getchar())<'0'||c>'9')
    		if(c=='-')f=-1;
    	res=c-48;
    	while((c=getchar())>='0'&&c<='9')
    		res=res*10+c-48;
    	return res*f;
    }
    inline int qpow(int d,int p){ d^p mod k
    	int ans=1;
    	while(p){
    		if(p&1){ // 找出 p 中二进制下为 1 的位
    			ans=ans*d%k;
    		}
    		p>>=1,d=d*d%k;	
    	}
    	return ans%k; // 大多时候可以不用取模 只有 k = 1 的时候需要这么做
    }
    # undef int
    int main(void){
    # define int long long
    	b=read(),p=read(),k=read();
    	printf("%lld^%lld mod %lld=%lld",b,p,k,qpow(b,p)); 
    	return 0;
    }
    

    整除

    基本知识

    对于非负整数 (a) 和正整数 (b),如果 (frac ab) 是整数(即: (a) 除以 (b) 的余数是 (0)),那么我们称 (b) 整除 (a),记作 (b mid a);称 (b)(a)约数(或称因数因子),(a)(b)倍数。例如:(2 mid 114514)(2)(114514) 的约数,(114514)(2) 的倍数。

    如果 (b) 不整除 (a),则记作 (b mid a)。例如 (114514 mid 1919810)

    (a) 除以 (b) 的余数记作 (a mod b)。例如 (9 mod 7=2,13 mod 4 =1)

    • 求出正整数 (n) 的所有约数

      容易发现 (n) 的约数成对出现,若 (i|n),则 (frac ni|n)

      考虑枚举 ([1,sqrt n]) 范围内的每一个正整数,逐个判断是否是 (n) 的约数。

      时间复杂度 (O(sqrt n))

      std::vector <int> A;
      for(int i=1;i*i<=n;++i){
      	if(i%n==0){
      		A.push_back(i);
      		if(i*i!=n){ // 对于 n 是完全平方数的情况 如果不加以判断会被记录两次
      			A.push_back(n/i);
      		}
      	}
      }
      
    • 求出 ([1,n]) 所有正整数的所有约数

      可以直接将上面的东西跑 (n) 遍,总复杂度 (O(nsqrt n))

      复杂度不够优秀。考虑枚举 (1)(n) 的所有正整数 (x),将 (x,2x,···) 的约数集合中添加一个元素 (x)

      这样总复杂度降到了 (O(n+frac n2+frac n3 ···· +1)=O(n ln n))

      std::vector <int> A[MAXN];
      
      for(int i=1;i<=n;++i){
      	for(rr int j=1;i*j<=n;++j){
      		A[i*j].push_back(i);
      	}
      }
      

    如果一个正整数只有 (1) 和它本身两个因子,那么我们称它为素数(或质数)。(1) 不是质数,(2) 是最小的质数。如果一个正整数的因子个数大于 (2),则我们称它为合数(1) 也不是合数。最小的合数是 (4)

    算术基本定理

    (n) 是正整数且 (>1)(p_i) 均为不同的素数,(c_i >0) 时,有唯一的 (p)(c) 使得

    [n=prod_{i=1}^{m} p_i^{c_i} ]

    其中 (c_i) 被称为 (n) 中素因子 (p_i)次数(指数),(p_i) 被称为 (n) 的素因子(质因子)。

    证明:如果有两个分解式 (n=prod_{i=1}^{m} p_i^{c_i})(n=prod_{i=1}^{m} {p'}_i^{{c'}_i})。不妨重排序列,设 (min{p_i} = p_1)(min{{p'}_i} = {p'}_1)(p_1 mid n Rightarrow p_1 mid prod_{i=1}^{m} {p'}_i^{{c'}_i}),则必定存在一个 ({p'}_i) 使得 (p_1 mid {p'}_i)。同理,存在 (p_j) 使得 ({p'}_1 mid p_j)。因为 ({p'}_i,p_j) 均为素数,所以可以改写为 ({p'}_i = p_1,p_j = {p'}_1)

    观察到 (p_1,{p'}_1) 均为序列中最小的数,因此 (p_1 = {p'}_1)。将这两项移除,可以递归地证明。

    • 推论
      (n) 的正约数个数为

      [prod_{i=1}^{m} (c_i+1) ]

      证明:感性理解,(n) 的约数的唯一分解中每个质数的次数不可能超过 (n) 的唯一分解中该质数的次数。只要满足了这个限制,次数可以随便取,取 (0)(不选这个质因子)到 (c_i) 中的任何指数都可以,因此是 (c_i + 1) 种取法。根据乘法原理,把每个质数的选法乘起来。

    (n) 的正约数和为

    [ prod_{i=1}^{m} (sum_{j=0}^{c_i} p_i^{j}) ]

    证明:和上面差不多,不再赘述。

    常用性质

    • 性质 (1)

      [d mid a ,d mid b Rightarrow d mid ax + by (x,y in mathbb{Z}) ]

      证明:显然。值得一提的是,(ax + by) 被称为 (a,b)线性组合

    • 引理 (1)

      (d | a) 的充分必要条件是 (d) 中任意一素因子的指数不超过 (a) 中该素因子的次数。

      这是显然的,不需要给出证明。

    • 引理 (1) 的推论

      ( ext{lcm} (a,b) imes gcd(a,b)= a imes b)

      证明:令 (a,b) 的唯一分解分别为 (a = prod limits_{i=1}^{m} p_i^{c_i},b = prod limits_{i=1}^{m} p_i ^{d_i}),此时对于每个 (i)(c_i,d_i) 不能同时为 (0)。由引理 (1) 可知,( ext{lcm}(a,b),gcd(a,b)) 的唯一分解分别为 ( ext{lcm}(a,b) = prod limits_{i=1}^{m} p_i ^{max(c_i,d_i)},gcd(a,b) = prodlimits_{i=1}^{m} p_i^{min(c_i,d_i)})

      显然对于任意整数 (a,b),有 (max(a,b) + min(a,b) = a+b),因此 ( ext{lcm}(a,b) imes gcd(a,b) = prodlimits_{i=1}^{m} p_i ^{max(c_i,d_i) + min(c_i,d_i)} = prodlimits_{i=1}^{m} p_i ^{c_i + d_i} = a imes b)

    • 性质 (2)

      [d mid a,d mid b Leftrightarrow d mid gcd(a,b) ]

      其中 (gcd(a,b))(a,b) 的最大公约数。

      证明:右推左很显然。因为 (d mid gcd(a,b)),又 (a,b) 均为 (gcd(a,b)) 的倍数,因此 (d) 也整除 (a,b)

      由引理 (1) 及其推论,设 (d = prod limits_{i=1}^{m} p_i^{e_i})(d mid a)(forall i,e_i leq c_i)(forall) 是「对于任意」符号,(c_i)(a) 的唯一分解中各个素因子的次数。

      同理可推得 (forall i,e_i leq d_i),其中 (d_i)(b) 的唯一分解中各个素因子的次数。

      两式联立,得 (forall i,e_i leq min(c_i,d_i)),即 (d mid gcd(a,b))

    • 性质 (3)

      [gcd(a,b) = gcd(b,a mod b) ]

      证明:考虑证明一个更强的条件,即 (gcd(a,b))(gcd(b, amod b)) 的约数集合相同。

      (a = kb + r(0 leq r < b))

      • 左推右:(d mid a,d mid b Rightarrow d mid r)

        由性质 (2),上面的条件和 (d mid gcd(a,b) Rightarrow d mid r) 等价。(r = a - kb),由性质 (1) 可知 (r)(a,b) 的一个线性组合,由已知得 (d mid a,d mid b),则 (d mid r) 显然成立。

      • 右推左:(d mid r ,d mid b Rightarrow d mid a)

        (a = kb + r),即 (r)(b) 的一个线性组合。剩下的部分和上面类似,不再证明。

    • 欧几里得算法求 GCD

      inline int gcd(int a,int b){
      	if(b==0)
      		return a;
      	return gcd(b,a%b);
      }
      

      不断利用性质 (3),直到 (b=0),这个时候返回 (a),即上一层时的 (b) 即可(因为上一层的 (b) 造成了 (a mod b = 0))。

  • 相关阅读:
    python-并发编程之多进程
    python-继承以及继承问题和多态
    python-面向对象的命名空间和组合
    python-初识面向对象
    python-模块与包
    python-异常处理
    ios开发相关网站
    优秀Android开源项目
    知名应用背后的第三方开源项目
    贪心算法
  • 原文地址:https://www.cnblogs.com/liuzongxin/p/14373801.html
Copyright © 2020-2023  润新知