• 洛谷网课数论


    老久以前的了,以前忘放上面了(差点丢了/jk)

    欧拉筛素数

    (O(n))筛法

    for(int i = 2; i <= n; i++) {
    	  if(vis[i] == 0) pre[tot++] = i;//存下每个素数
    	for(int j = 0; j <= tot, i * pre[j] <= n; j++){
    		  vis[pre[j] * i] = 1;
    		  if (i % pre[j] == 0){//保证每个数只筛一次
    		  	  break;
    		  } 
    	}
    }
    

    保证每个数被它的最小质因子筛掉

    为什么 (i % pre[j] == 0) 就 break ??

    证明

    • (pre) 数组中的素数是递增的

    • (i) 能整除 (pre_j),那么 (i * pre_{j+1}) 这个合数肯定被 (pre_j) 乘以某个数筛掉

    • 因为 (i) 中含有 (pre_j) , (pre_j)(pre_{j+1}) 小,即 (i = k * pre_j)

    • 那么 (i * pre_{j+1} = (k * pre_j) * pre_{j + 1})

    • (pre_{j + 1} * k = k') 所以 (i * pre_{j +1} = k'*pre_j)

    同余

    在模意义下,加减乘都不会影响答案

    加减很显然

    证明乘??

    • 目标:((x*y) ~mod~ p~= (x ~mod~ p) * (y ~mod~ p))

    • (x = a_1*p + b_1,y = a_2 * p + b_2)

    • (x*y = (a_1*a_2 * p + a_1 * b_2 + b_1*a_2)*p + b_1*b_2)

    • 很显然 (x*y)(p) 取模就是 ((b_1*b_2))(p) 取模

    栗题

    P4942 小凯的数字

    我们把最后组合起来的数拆成每一位相加的形式,因为是在模9的意义下,所以 (10^n~mod~ 9 = 1) 所以最后答案也就是把每一位相加都取模

    辗转相除法证明

    • (gcd(a, b) = gcd(b, a~mod~b))

    • (g|a)(g|b), (r = a - k * b = a~mod~b)

    • 可得 (frac{r}{g}= frac{a}{g} - frac{k*b}{g})

    • 看得出,右边显然是个整数,所以 (g | r)

    exgcd

    可解方程

    [ax + by = gcd(a, b) ]

    (x,y)

    • (a' = b, b' = a~mod~ b)

    • (a'x + b'y = gcd(a', b'))

    • 根据 (gcd)(gcd(a', b') = gcd(a, b))

    • (a'x + b'y = gcd(a, b))

    • (bx + (a - b * lfloorfrac{a}{b} floor)*y = gcd(a, b))

    • (ay + b(x - lfloor frac{a}{b} floor*y) = gcd(a,b))

      ( herefore x = y, y = x - lfloor frac{a}{b} floor*y)

    一个回溯的过程

    应用(扩欧解不定方程)

    解形如 (ax+by=c) 的不定方程

    先根据 (exgcd) 求出 (ax + by = gcd(a, b)) 的一组解 (x_0, y_0)

    根据裴蜀定理,当 (c ~mod~ gcd(a, b) = 0) 的时候,此方程有解

    求解

    • (d = gcd(a , b))

    • 方程两边同时乘以 (frac{c}{d}) , 得到 (a*frac{c}{d}x_0 + b*frac{c}{d}y_0 = c)

    • 然后就有了一组解 (x_1 = frac{c}{d}x_0, x_2 = frac{c}{d}y_0)

    求最小整数解(使 (x_1) 最小化)

    现在已经由 (exgcd) 求得任意一组 (x, y)

    为了满足等式成立

    那么 (x) 最小正整数解为 ((x + b) \% b)

    为什么??

    可以这样想:

    先在已经求出一组解使得 (ax + by = 1) 也就是 (y = frac{1 - ax}{b})

    因为 (y) 是整数,所以此时 (b | 1 - ax) 现在想让 (x) 改变,如果 (x) 改变的值不是 (b) 的倍数,那就会使得 (1 - ax) 不能整除 (b)(y) 就不是整数了,所以 (x) 的改变量一定是 (b) 的倍数才可以

    code

    d = exgcd(a, b, x, y);//返回的是 a,b 的 gcd
    if(c % d == 0)
    { 
        x *= c / d; 
        t = b / d;
        t = abs(t);
        x = (x % t + t) % t;
        printf("%d
    ", x); 
    }
    

    栗题

    P1516 青蛙的约会

    /*
    work by:Ariel_
    */
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #define int long long
    using namespace std;
    
    int read(){
        int x = 0,f = 1; char c = getchar();
    	while(c < '0'||c > '9') {if(c == '-') f = -1; c = getchar();}
    	while(c >= '0' && c <= '9') {x = x*10 + c - '0'; c = getchar();}
    	return x*f;
    }
    int x_1, y_1;
    int exgcd(int a, int b, int &x1, int &y1){
       if(!b){
          x_1 = 1, y_1 = 0;
       	  return a;
       }
       int d = exgcd(b, a % b, x_1, y_1);
       int t = x_1; x_1 = y_1, y_1 = t - a / b * y_1;
       return d;
    }
    int x, y, m, n, l;
    signed main(void){
        x = read(), y = read(), m = read(), n = read(), l = read();
        int a = n - m, b = l, c = x - y;
        int d = exgcd(a, b, x_1, y_1);
        if(c % d == 0) {
          x_1 *= c / d;
          int t = b / d;
          t = abs(t);
          x_1 = (x_1 % t + t) % t;
          printf("%lld", x_1);
    	}
    	else printf("Impossible");
    	return 0;
    }
    

    中国剩余定理

    (m_1, m_2, m_3……m_n) 是两两互质的正整数,有同余方程组

    [egin{cases} x equiv a_1 (mod~m_1)\ x equiv a_2 (mod~m_2)\ ……\ x equiv a_n (mod~m_n)\ end{cases} ]

    (x) ?

    (M = prod_{i=1}^{n} m_i)(M_i = M/m_i)(t_i) 是线性同余方程 (M_it_i equiv 1(mod ~m_i)) 的一个解

    (x = sum^{n}_{i = 1}a_iM_it_i)

    证明:

    根据一个定理

    两数不能整除,若除数扩大(或缩小)了几倍,而被除数不变,则其商和余数也同时扩大(或缩小)相同的倍数(余数必小于除数)

    (证明: 假设 a % b = d,a / b = k, a = kb + d, a - kd = d,a,b 同时扩大 z 倍,得到 z(a - kd) = zd;)

    所以可以得出 (a_iM_it_i equiv a_i(mod~~m_i))

    显然 $ M_i$ 除了可以 (m_i) 之外都可以整除其他 (m)

    (ecause forall k ot= i,a_iM_it_i = 0(mod~m_k))

    (ecause a_iM_it_iequiv a_i(~mod~m_i))

    所以带入原式得到:(x = sum^{n}_{i = 1}a_iM_it_i)

    可以结合 despair_ghost 这篇博客理解,讲的特别好

    实现

    回到上面的式子

    • (M_it_i equiv 1(mod ~m_i))

    • 可以得到 (M_it_i - k*m = 1)

    • (M_i, m) 都是定值,因为 (M_i, m) 互质,所以就可以 (exgcd) 求解了

    【模板】中国剩余定理(CRT)/曹冲养猪

    /*
    work by:Ariel_
    */
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #define int long long
    using namespace std;
    const int N = 20;
    int read(){
        int x = 0,f = 1; char c = getchar();
    	while(c < '0'||c > '9') {if(c == '-') f = -1; c = getchar();}
    	while(c >= '0' && c <= '9') {x = x*10 + c - '0'; c = getchar();}
    	return x*f;
    }
    int Ans, M = 1;
    int n, m[N], a[N];
    void exgcd(int a, int b, int &d, int &x, int &y){
        if (!b){d = a,x = 1, y = 0;return;}
          exgcd(b, a % b, d, x, y);
          int t = x; x = y, y = t - (a / b) * y;
    } 
    void Int_China() {
       int Mi, x, y, d; 
       for (int i = 1; i <= n; i++) {	    
    	    Mi = M / m[i];
       	    exgcd(Mi, m[i], d, x, y);
       	    Ans = ((Ans + x * Mi * a[i]) % M + M) % M; 
       }
    }
    signed main(void) {
        n = read();
        for(int i = 1; i <= n; i++) m[i] = read(), a[i] = read(), M *= m[i];
    	Int_China();
        printf("%lld
    ",(Ans + M) % M );
    	return 0;
    }
    
    

    扩展CRT

    (x) ?

    [egin{cases} x equiv a_1 (mod~m_1)\ x equiv a_2 (mod~m_2)\ ……\ x equiv a_n (mod~m_n)\ end{cases} ]

    和中国剩余定理类似,就是没有互质的条件了

    数学归纳法:

    假设已经求出前 (k-1) 个方程组成的同余方程组的一个解为 (x)

    且有 (M=prod_{i=1}^{ileq k-1})

    则前 (k-1) 个方程的方程组通解为 (x+i*M(iin Z))

    那么对于加入第 (k) 个方程后的方程组

    我们就是要求一个正整数 (t),使得 (x+t*M equiv a_k(mod~m_k))

    转化一下上述式子得 (t*M equiv a_k-x(mod m_k))

    对于这个式子我们已经可以通过扩展欧几里得求解t

    若该同余式无解,则整个方程组无解, 若有,则前 (k) 个同余式组成的方程组的一个解解为(x_k=x+t*M)

    乘法逆元

    解决模意义下没法进行除法的问题

    已知 (a|b)(frac{b}{a}mod~m)

    设法把除法转化到乘法上去

    找一个 (a^{-1}) 满足 $a*a^{-1}equiv1(mod~m) $

    换个形式

    (ax equiv 1(mod~m))

    根据扩展中国剩余定理就可以求出来了

    可与忽视 p 不为质数的情况:只要 gcd(a,m) 不为 1,那么 a 的逆元就不存在

    欧拉函数

    欧拉函数 (varphi(m)) 表示不超过 (m) 且和 (m) 互素的正整数的个数

    (m = prod p_i^{a_i})

    求单值欧拉函数?

    公式:

    [varphi(n) = n*prod^k_{i = 1}(1 - frac{1}{p_i}) ]

    (p_1,p_2…p_k)(n) 的所有质因子

    证明

    考虑容斥

    因为要求与 (n) 互质的数的个数,因为在 (n) 之内,每一个 (p_i) 的倍数分布是均匀的, (n) 之内有 (frac{1}{p_i}) 的数是 (p_i) 的倍数,因此有 (n*(1 - frac{1}{p_i})) 个数不是 (p_i) 的倍数,同理有 (n*(1 - frac{1}{p_j})) 个数不是 (p_j) 的倍数,根据乘法原理,有 (n*(1 - frac{1}{p_i})* (1 - frac{1}{p_j})) 个数既不是 (p_i) 的倍数,也不是 (p_j) 的倍数。进而就可以推广得到有 (n*prod^{k}_{i = 1}(1 - p_i)) 个数与 (n) 互质 (有点生物算遗传的意思)

    严谨证明(来自 (attack) 大佬)

    1.当 (n=1)

    很明显,答案为 (1)

    2.当 (n) 为质数时

    根据素数的定义,答案为 (n−1)

    3.当 (n) 为合数时

    我们已经知道了 (n) 为素数的情况

    (n) 进行质因数分解

    (n=a^{p_{1}^1}∗a^{p_{2}^{2}}...∗a^{p_{k}^k})

    假设 (k=1)

    那么 (varphi(p^k)=p^k−p^{k−1})

    证明:考虑容斥,与一个数互素的数的个数就是这个数减去与它不互素的数的个数

    因为 (p) 是素数,所以在 (p^k) 中与其不互素的数为 (1∗p,2∗p....p^{k−1}∗p),有(p^{k−1})

    (k eq 1)

    • (varphi(n) = varphi(a_1^{p_{1}^1}∗a_2^{p_{2}^{2}}...∗a_k^{p_{k}^k}))

    • (prod^k_{i = 1}a_i^{p_i} - a_{i}^{p_{i - 1}})

    • (prod^k_{i = 1}a_i^{p_i}(1-frac{1}{p_i}))

    • (n*prod^k_{i = 1}(1-frac{1}{p_i}))

    单值求法:若 (gcd(a, b) = 1) 就更新 (phi_n) 的值

    (1-n)(varphi(i))

    埃氏筛

    (O(n)) 扫一遍,每扫到一个质数,就更新一下它的所有的倍数

    时间复杂度:(O(n*ln ln n))

    /*
    work by:Ariel_
    */
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    using namespace std;
    const int N = 1e5 + 4;
    int read(){
        int x = 0,f = 1; char c = getchar();
    	while(c < '0'||c > '9') {if(c == '-') f = -1; c = getchar();}
    	while(c >= '0' && c <= '9') {x = x*10 + c - '0'; c = getchar();}
    	return x*f;
    }
    int n, phi[N];
    int main(void){
        n = read();
        for (int i = 1; i <= n; i++) phi[i] = i;
        for (int i = 2; i <= n; i++){
        	if(phi[i] == i) {
        	   for (int j = i; j <= n; j += i)
        		  phi[j] = phi[j] / i * (i - 1);
    		}
    	}
    	for(int i = 1; i <= n; i++){
    		printf("%d
    ", phi[i]);
    	}
    	return 0;
    }
    

    康托展开

    康托展开是全排列到自然数的一一映射,该自然数可以代表该序列在全排列中的排名(从0开始)

    正康托展开:全排列到名次

    (a_5 = lbrace2,3,5,1,4 brace)

    求该序列的名次

    考虑比它小的序列

    (1*4!+1*3!+2*2!+0+0 = 34) 排列组合

    可以这么理解:后面比当前小的数的个数*后面数的全排列

    逆康托展开:名次到全排列

    (1*4!+1*3!+2*2!+0+0 = 34)

    得到 (a_5 = lbrace2,3,5,1,4 brace)

    卢卡斯定理/Lucas 定理

    这直接给出公式,证明过程涉及二项式内容,感性理解 = =

    (C_n^m = Lucas(n, m, p))

    (Lucas(n, m, p) = C(n\%p,m\%p) * Lucas(frac{n}{p }, frac{m}{p}, p))

    (Lucas(x, 0, p) = 1, C(a, b) = (frac{a!}{(a - b)!}^{p - 2})~mod~p)

    int n, m, p, a[N];//a:阶乘 
    int qpow(int x, int y) {
       int tmp = 1;
       while(y) {
       	 if (y & 1) tmp = (tmp * x) % p;
       	 y >>= 1;
       	 x = (x * x) % p;
       }
       return tmp;
    }
    int C(int n, int m) {
       if (m > n) return 0;
       return (a[n] * qpow(a[m], p - 2) % p * qpow(a[n - m], p - 2) % p) % p;
    }
    int Lucas(int n, int m) {
    	if (!m) return 1;
        return C(n % p, m % p) * Lucas(n / p, m / p) % p;
    }
    signed main(){
       int T = read();
       while(T--) {
       	 a[0] = 1;
       	 n = read(), m = read(), p = read();  
         for (int i = 1; i <= p; i++) a[i] = a[i - 1] * i % p;
    	 printf("%lld
    ", Lucas(n + m, n)); 
       }
       return 0;
    }
    
  • 相关阅读:
    JVM-程序编译与代码晚期(运行期)优化
    JVM-程序编译与代码早期(编译期)优化
    JVM-字节码执行引擎
    JVM-类加载机制
    JVM-字节码指令
    JVM-class文件完全解析-属性表集合
    zabbix配置邮箱报警功能
    Linux Shell 返回值之 PIPESTATUS
    SSH远程登录原理
    Zabbix监控USG6300防火墙及交换机
  • 原文地址:https://www.cnblogs.com/Arielzz/p/14932638.html
Copyright © 2020-2023  润新知