2023-10-30 23:07阅读: 27评论: 0推荐: 0

数论学习笔记

主要参考 OI-Note Chapter4.1 整除与同余 - 知乎

整除

a/b(b0) 为整数,则称 b 整除 a ,记作 ba 。若 a/bc/b 的余数相等,则称 a,cb 同余。

同余

关于同余,有以下命题等价:

  1. ab 是模 d 同余的。
  2. 存在某个整数 n ,使 a=b+nd
  3. d 整除 ab

由此可以轻易推出以下性质:

同余的基本性质

  • ab(modp)cd(modp)a±cb±d

  • ab(modp)akbk(modpk)

  • kpab(modp)ab(modk)

模运算基本性质

  • (a+b)modp=((amodp)+(bmodp))modp

  • abmodp=(amodp)(bmodp)modp

  • amodkbmodb=amodb

欧几里得辗转相除法(gcd)

给定整数 a,b ,将它们的最大公因数记作 gcd(a,b) ,欧几里得辗转相除法便是用来求 gcd(a,b) 的,因而简称 gcd
a,b 的公因数集合为 Tb,amodb 的公因数集合为 T ,有 T=T 。证明:
uTa=xu,b=yuamodb=aba/b=xuyu(a/b)=u[xy(a/b)] ,所以 uamodbTT
同样的,记 vTb=nv,amodb=mva=amodb+ba/b=mv+nv(a/b)=v[m+na/b] ,所以 vaTT

综上, gcd(a,b)=gcd(b,amodb) ,因而可以直接递归求解。时间复杂度为 log(n)

边界:当 b=0 时,gcd(a,b)=a

code:

int gcd(int a, int b){
    return b ? gcd(b, a % b) : a;
}

扩展欧几里得(exgcd)

由欧几里得辗转相除法,我们可以运用这个算法在求最大公因数的同时求出满足 ax+by=gcd(a,b) 的正整数解 x,y

对于边界条件 b=0x=1,y=0 就是一组合法解(当然,此处 y 可以取任意数)。那么,设 a=b,b=amodb ,且我们已经求出 x0,y0 满足 ax0+by0=gcd(a,b) ,于是:

gcd(a,b)=gcd(a,b)=ax0+by0=bx0+(amodb)y0=bx0+(aba/b)y0=ay0+b(x0a/by0)

因此,满足 ax+by=gcd(a,b)x=y0,y=x0a/by0

code:

int exgcd(int a, int b, int &x, int &y){
    if(!b){
        x = 1, y = 0;
        return a;
    }
    int a1 = exgcd(b, a % b, x, y);
    int x1 = y, y1 = x - a / b * y;
    x = x1, y = y1;
    return a1;
}

乘法逆元

对于一个数 a ,若 ab=1 ,那么我们称 a 的逆元为 b ,记作 a1 。在同余中,则是若 ab1 , 则 ba 的逆元。通常,我们通过找到 a 的逆元来避免除法。给定 ab ,我们需要求出 x 满足 ax1(modb)

逆元唯一性定理:逆元若存在,则总是唯一的。
反证法。设有 xy 使得 axay1 ,则有 axyax(y)y ,且 axyay(x)x , 则 xy ,矛盾。
逆元存在性定理:在模 m 下,当且仅当 am 时,a 的逆元存在。后续补充证明。

事实上, ax1(modb) 等价于 ax+by=gcd(a,b) 。由逆元存在性定理,ab 时, ax+by=1 。所以 ax=1byax1 。而若 ax1(modb) ,则 ax10(modb) ,所以令 y=ax1b , 就有 ax+by=1 。因此这两者等价。我们就可以直接使用exgcd求出 a 在模 b 下的乘法逆元。不过有一个需要注意的是,使用exgcd求出来的逆元可能是负数,输出时加一个 (x % p + p) % p 就好啦。

线性求逆元

我们知道 a×a11

p=n×i+r ,就有:

n×i+r0(modp)

同时乘以 i1,r1 得:

n×r1+i10

移项得:

i1n×r1

i1p/i(pmodi)1

又因为 pmodii 小,我们就可以 O(n) 求逆元了!

裴蜀定理

不定方程 ax+by=c 有整数解 x,y 的充要条件是 gcd(a,b)c
证明:
gcd(a,b)c 时,我们可以先用exgcd求出ax+by=gcd(a,b) 的解,然后同时乘以  c/gcd(a,b) 即可得出解。
gcd(a,b)c 时,在该方程两边同时除以 gcd(a,b) ,发现方程左边为整数,右边为分数,则必定无解。

由此,我们就可以运用裴蜀定理直接证明逆元存在性定理了。

另外, 若 cm,acbc(modm) ,运用逆元可以得出 ab 。这也是一条同余的性质。

P5656 【模板】二元一次不定方程 (exgcd)

虽然是个板子题,但是非常恶心。

无解情况裴蜀定理判定即可。
对于有解:
首先我们就可以通过exgcd求出满足 ax+by=c 的一组整数解 x0,y0 ,记 d=gcd(a,b) 。怎么由一组特解推出通解呢?

由观察可知,当 x 变大时, y 会变小。因此设 x0+kp,y0kq 为另外一组解 ( p,q 为一个定值),我们通过改变 k 来求得每一组解。这个时候可能聪明的你会感觉到有些不对劲:为什么 pq 的系数绝对值刚好都是 k 呢?就不能 x+(k+1)p,y0kq 吗? p,q 又可以是什么定值呢?我们来证明一下:
不管k,我们可以列出方程组:

{ax0+by0=ca(x0+p)+b(y0q)=c

可以解得 ap=bq,p=bqa,q=apb 。也就是 p,q 需要满足这个等式才能构造出另外一组解。但还不够,我们需要 p,q 为最小的正整数 p,q ,以此来通过调整系数 k 求出每一组解,要求 abqbap 。所以 bpmin=lcm(a,b)=abd (此处详见下面的最大公倍数),pmin=ad 。同理 qmin=bd 。我们就求出了 p,q 的最小正整数。

我们设另一组解为 x0+np,y0mq ,则:

{ax0+by0=ca(x0+np)+b(y0mq)=c

解得 anp=bmq 。又由上面的 ap=bq ,我们就会发现 n=m ,所以 k=n=m 就好啦!

之后具体细节分类讨论即可(然后就调了一上午) 。推荐题解第一篇,感觉最简单易懂!(加 longlong, 不然就会在不知不觉中浪费1个小时QAQ)

#include<bits/stdc++.h>
using namespace std;

#define int long long
#define mo 19260817

int a, b, c;

int exgcd(int a, int b, int &x, int &y){
  if(!b){
    x = 1, y = 0;
    return a;
  }
  int a1 = exgcd(b, a % b, x, y);
  int x1 = y, y1 = x - a / b * y;
  x = x1, y = y1;
  return a1;
}

signed main(){
//   freopen("shuju.in", "r", stdin);
  ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
  int TN;
  cin >> TN;
  while(TN--){
    cin >> a >> b >> c;
    int x, y;
    int gcd = exgcd(a, b, x, y);
    x *= c / gcd, y *= c / gcd;
    int p = b / gcd, q = a / gcd;
    if(c % gcd) {
      cout << -1 << endl;
      continue;
    }
    int sl = ceil((1.0 - x) / p), sr = floor((y - 1.0) / q);
    if(sl > sr) {
      cout << x + sl * p << " " << y - sr * q; 
    }
    else{
      cout << sr - sl + 1 << " ";
      cout << x + sl * p << " " << y - sr * q << " " << x + sr * p << " " << y - sl * q;
    }
    cout << endl;
  }
  return 0;
}

欧拉定理

am 时, aφ(m)1(modm)
[[欧拉函数&欧拉定理]]

详细证明可见 欧拉函数 & 欧拉定理
证明:
我们构造一个数 k ,只需使得 kaφ(m)k ,且 km 即可证明欧拉定理。设在模 m 下的剩余系中与 m 互质的数的集合为 {b1,b2,...bn}(n=φ(m)) ,令 k=b1b2...bn ,这时 km 。又因为 am , 所以集合 {ab1,ab2,...,abn} 中每个数都与 m 互质,即 {b1,b2,...bn}={ab1,ab2,...,abn} 。那 ab1ab2...abn=aφ(m)kk 。证毕。

由欧拉定理,我们能直接得出费马小定理,实际上欧拉定理就是费马小定理的推广。

最小公倍数

我们已经知道可以通过 gcd 求出 a,b 的最大公因数,那么有没有类似的快捷方法求出 a,b 的最大公倍数呢?

当然有!通过算术基本定理,我们将 a,b 表示成如下形式:

a=p1n1p2n2...pxnx

b=p1m1p2m2...pxmx

其中 p 属于 a,b 的所有质因数集合。

那么

gcd(a,b)=p1min(n1,m1)p2min(n2,m2)...pxmin(nx,mx)

lcm(a,b)=p1max(n1,m1)p2max(n2,m2)...pxmax(nx,mx)

于是

lcm=abgcd(a,b)

Lucas定理

前置知识:二项式定理
可以看看容斥定理及二项式反演

Lucas定理:

(nm)(npmp)×(nmodpmmodp)(modp)

其中 p 是质数。
可以看成是 n,mp 进制下各数码的组合乘积。

证明(不要被式子吓到啦,很简单的):

(nm) 可以看成 (1+x)nxm 项的系数,我们就尝试写一下:

(1+x)n=(1+x)pnp(1+x)nmodp

因为 p 是质数,所以对于 i[1,p1]i 在模 p 下均有逆元,(pi)=pi(p1i1)0(modp)

(1+x)p=i=0p(pi)xi1+xp

因为中间的系数在模 p 下为 0 ,在 (1+x)n 中相加不会对模 pxm 项的系数有所影响,就可以直接忽略。于是我们继续:

(1+x)pnp(1+x)nmodp(1+xp)np(1+x)nmodp=i=0np(npi)xpii=0nmodp(nmodpi)xi

发现这个式子要想凑出 xm 那一项,因为左边只能提供 p 的倍数次项,右边只能提供模 p 的余数的项,所以对 xm 的系数有贡献的项必须是是左边提供 mp 的项, 右边提供 mmodp 的项,所以 xm 项的系数在模 p 下就是

(npmp)(nmodpmmodp)

证完!

素数的线性筛法

为了让一个合数只被标记一次,我们设法让一个合数只会被它的最小素因子标记一次。

vector<int> p;
int tag[N];
void init(int n){
	tag[1] = 1;
	for(int i = 2; i <= n; i ++){
		if(!tag[i]) p.push_back(i);
		for(auto j : p){
			if(j * i > n) break;
			tag[i * j] = 1;
			if(i % j == 0) break;
			//这里我们假设i = k*j, 下一个素数为g, g > j
			//i*g = k*j*g, 因为k*g > i,所以i*g在之后循环到k*g时被标记,这里就不用标记了
		}
	}
}

整除分块

能够在 O(n) 的时间内求出 i=1nni

正常肯定是 O(n) 的,但是考虑一下如何加快。随便画一个反比例函数图像,会发现有对于每个 i ,几乎都会有一段连续区间 ni 的值是相同的。结论是,对于 i 所在的块,块的右端点是 nni

另外一个结论,i=1n[xi]=nx

int g(int n){
  int ans = 0;
  for(int l = 1, r; l <= n; l = r + 1){
    r = n / (n / l);
    ans += (r - l + 1) * n / l;
  }
  return ans;
}

威尔逊定理

p 为素数等价于

(p1)!1(modp)

本文作者:yduck

本文链接:https://www.cnblogs.com/yduck/p/17799163.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   星影流灿  阅读(27)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起