洛谷网课数论

摘要:
这是很久以前的事了。我忘了戴(我差点丢了/jk)。通过同余除法证明了目录欧拉筛素数。扩展了中国剩余定理。CRT乘法是逆的。Euler函数展开。卢卡斯定理/卢卡斯定理是欧拉筛素数(O(n))。(int i=2;i<=n;i++){if(vis[i]==0)pre[tot+]=i;//保存(int j=0;j<=tot,i*pre[j]<=n;j++){vis[pre的每个素数[

老久以前的了,以前忘放上面了(差点丢了/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;
}

免责声明:文章转载自《洛谷网课数论》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇django的数据库ORM进阶操作C#使用zookeeper下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

【数论】C.Orac and LCM

C.Orac and LCM 题意:给定一个长度为(n)的数组,求(gcd{{lcm(a_i,a_j)|i<j}}) 思路: 对于(a_1),其产生的(lcm)有(lcm(a_1,a_2)、lcm(a_1,a_3)、...lcm(a_1,a_n)) 则它们的最大公因数(gcd_1=gcd(lcm(a_1,a_2)、lcm(a_1,a_3)、..lcm...

HDU 2503 (数论,最大公约数)

a/b + c/d Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 14895    Accepted Submission(s): 7761 Problem Description 给你2个分数,...

图论介绍(Graph Theory)

1 图论概述 1.1 发展历史 第一阶段: 1736:欧拉发表首篇关于图论的文章,研究了哥尼斯堡七桥问题,被称为图论之父 1750:提出了拓扑学的第一个定理,多面体欧拉公式:V-E+F=2 第二阶段(19~20世纪): 1852: Francis Guthrie提出四色问题 1856: Thomas P. Kirkman & William R.H...

素数算法大全

注意: 如果没有特殊说明, 以下讨论的都是针对n为素数时的时间复杂度 1. 根据概念判断: 如果一个正整数只有两个因子, 1和p,则称p为素数. 代码: bool isPrime(int n) { if(n < 2) return false; for(int i = 2; i < n; ++i) if...

中国剩余定理(孙子定理)及实现----原理详解

中国剩余定理(孙子定理) 转载请注明:http://blog.csdn.net/passion_acmer/article/details/52290361 问题:今有物不知其数,三三数之剩二,五五数之剩三,七七数之剩二。问物几何? 解法:三三数之剩二,置一百四十;五五数之剩三,置六十三;七七数之剩二,置二十三,并之,得二百三十三。以二百一十减之,即...

深度理解 原码, 反码, 补码

本篇文章讲解了计算机的原码, 反码和补码. 并且进行了深入探求了为何要使用反码和补码, 以及更进一步的论证了为何可以用反码, 补码的加法计算原码的减法. 论证部分如有不对的地方请各位牛人帮忙指正! 希望本文对大家学习计算机基础有所帮助! 一. 机器数和真值 在学习原码, 反码和补码之前, 需要先了解机器数和真值的概念. 1、机器数 一个数在计算机...