既然选择了远方,|

H_W_Y

园龄:1年11个月粉丝:28关注:15

Number Theory(1)

202404

0 前言

离散数学是本书的重点,而整数又是离散数学的中心议题。数论 是讨论整数性质的重要数学分支,因此我们要来探索它。

​ ——《具体数学》第四章


标有 * 的为拓展内容,或者说比较难的题目,但它们都非常有趣。

部分题目的代码是洛谷的提交记录,阅读这篇文章的大多数同学都该能看(若有需要可以找我要)。

没看懂的地方直接问,不排除我会存在手误。


1 基本概念

1.1 整除

两个整数相除,我们自然想去讨论商是否是一个整数。小学生常常期待这个,因为整数比分数写起来好看。

这引出了数论中的基础概念:整除

如果 m>0n/m 是一个整数,我们就说 m 整除 n(或者 nm 整除)。这个性质奠定了整个数论的基础,所以我们赋予它一个特殊的记号会更方便,记为

mnm>0kn=mk

同样的,如果 m 不整除 n,我们就写成 mn


1.2 整值函数

整数是离散数学的支柱,我们常常需要将分数或者任意的实数转换到整数。 ——《具体数学》第三章

我们首先来讨论 (floor,最大整数)函数和 (ceiling,最小整数)函数,对于所有实数 x,其定义如下:

x=xx=x

俗称 下取整上取整,底和顶也会在数论中有许多应用,在后续的部分内容中我们也会用到。


xx 之间的差称之为 x分数部分,它在应用中经常出现,所以也值得拥有自己的记号:

{x}=xx

我们有时称 xx整数部分,因为 x=x+{x},这也是一个实数的表示方法。


尝试在这里就证明一个非常有趣且有用的事实:如果 mn 是整数且分母 n 为正,则

x+mn=x+mnx+mn=x+mn

f(x) 是任意一个具有如下性质且在一个实数区间连续的单调递增函数

f(x)=x=

于是只要 f(x),f(x),f(x) 有定义,我们就有

f(x)=f(x)f(x)=f(x)

画出图来容易发现这是显然的,这里不做证明。

我们发现其实 f(x)=x+mn 也具有上面要求的 f(x) 的性质,于是我们就证得了上面的结论。


1.3 带余除法

mn 是正整数时,nm 除的商是 n/m。而大多数情况我们都不能做到 m 整除 n,所以这个除法的余数也有一个简单的记号,很方便,我们称它是 nmodm。基本公式

n=mn/m+nmodm

告诉我们,可以将 nmodm 表示成 nmn/m,我们可以将它推广到任意实数:

xmody=xyx/y,y0

y=0 呢?为了避免用 0 作除数,也为了完整起见,我们可以定义:

xmod0=x

这里的 mod 就被定义成了一个二元运算,也就是 取模。容易发现 xmody 是在 [0,y1] 中的一个值。

我们约定 mod 比加法或者减法的优先级高。同时,分配律mod 最重要的代数性质,对于所有实数 c,x,y,我们有

c(xmody)=(cx)mod(cy)

容易从定义证明这个法则,因为如果 cy0,则有

c(xmody)=c(xyx/y)=cxcycx/cy=cxmodcy

且模数为零时该式显然也为真(两边都是 cx)。


关于 mod,还有一些非常显然的性质。

  • (a+b)modp=((amodp)+(bmodp))modp
  • (ab)modp=((amodp)(bmodp))modp
  • amodbkmodb=amodb

前两条定义了 mod 的加法和乘法,第三条则是关于模数的一个转化。

这些性质都是耳熟能详的,这里也不做证明。


1.4 同余关系

模运算是数论提供的一种重要工具,我们在上面把它当成二元运算使用,而在这里我们也可以把 mod 运用到整个方程上面,为此,我们使用稍不同的记号会更加方便:

ab(modm)amodm=bmodm

由于 xmodmx 相差 m 的倍数,因而我们可以用另一种方式来解读同余式:

ab(modm)abm

同余符号 看起来很像 =,因为同余式与方程非常相像。例如,我们将同余式元素相加减,仍保持同余关系:

abcda±cb±d(modm)

乘法同样有效,只要处理的对象是整数:

abcdacbd(modm)

证明直接作差:acbd=(ab)c+b(cd)。反复利用乘法性质,我们可以取幂:

abanbn(modm),a,b,n0

这样一来,我们对方程所习惯做的大多数代数运算对同余式都可以运用,但并不是所有运算。除法运算显然不在其中。

以下部分可能会用到之后要讲的内容。

如果 adbd(modm),我们不能总断言 ab(modm)。例如 3×25×2(mod4),但 35(mod4)

然而在 dm 互素的情形中,我们可以挽救这一消元的性质:

adbd(modm)ab(modm),a,b,d,m,dm

它的证明是直接在式子两边乘上 d 的逆元,进而,我们可以把这个东西推广到更为一般的法则,它尽可能小地改变模:

adbd(modm)ab(modmgcd(d,m))

进一步观察改变模的想法,我们可以得到另外一些式子:

ab(modmd)ab(modm)

反过来,如果我们知道对于两个小的模数有 ab,是否能断定对于一个更大的模数有 ab 呢?这个规则是

ab(modm)ab(modn)ab(modlcm(n,m))

因为如果 abmn 的倍数,就一定是 lcm(n,m) 的倍数。

注意关注这个式子的特殊情形 mn,这在之后的 中国剩余定理 颇有应用。


2 欧几里得

有着我们熟悉的 gcd(a,b)lcm(a,b)

2.1 欧几里得算法(gcd)

欧几里得算法,又称辗转相除法,常用递归得到最大公因数。

gcd(a,b)=gcd(b,amodb)

而这是容易简单证明的:

a=kb+ra,b 的最大公因数为 d,那么一定满足 d|a,d|b

于是 r=akb,我们将两边同时 ÷d 就可以得到 rd=adkbd

由于 d|a,d|b,所以上面式子的右边一定是整数,于是 rd 也是一个整数。

这样一来,d 也是 amodb=r 的因数,得证。


CF1806F2 GCD Master (hard version)

给定 n,m 和一个长度为 n 的序列 {ai}(aim)

定义一次对一个长度为 m 的序列的操作为,选择序列中两个下标 1i<jm,删去 aiaj,然后在序列末端加入 gcd(ai,aj)

例如,对于 [7,6,2],一次操作可以选择下标 23,这样操作后,序列变成 [7,2]

给定 k,求对序列 {ai} 执行 k 次操作后得到序列中的数的和的最大值。

1K<n106,1m1018

感谢🐨的供题。


首先我们考虑有相同的数如何处理?

发现我们对相同的数操作,相当于删去一个,进而在后面的讨论中我们发现其实相同的数并无意义,所以我们最后枚举删了几个相同的数就可以了,于是我们就只需要对不相同的数进行操作即可。


考虑每一次操作,删除两个数 x,y 并加入 gcd(x,y),而当我们再把 gcd(x,y)z 操作时就等价于删除 x,y,z 并加入 gcd(x,y,z)

那么这个问题就被转化成把数分成若干组,删去这一组并加上它们的 gcd,对于一组 k 个元素,它们需要的操作次数是 k1 次。

我们如何让分组最优?不难猜到把所有的分成一组。

假设现在我们存在两组 ST,它们的 gcd 分别是 x,y 满足 xy,那么此时的贡献是

x+yiSaiiTai

我们尝试合并两组,也就是把 S 中最大的元素拿走,gcd 变成 gcd(x,y),由于数都是不同的,所以 S 中的 mx2x,那么现在的贡献就是

gcd(x,y)+mxiSaiiTai

由于 mx2x,所以

gcd(x,y)+mx>x+y

一定是成立的,所以合并两组一定更优,于是我们就得到了这一结论。


这样我们就可以取枚举 gcd,选出其和最小的集合,再枚举一下删了多少个相同的数,直接减去它们的和就可以在 O(mlogm) 的时间复杂度解决问题。(我们发现把相同的数加到删除的集合和直接删除是本质相同的,所以若删去 p 个相同的数,我们只需要把前 p 小的提出来删掉,具体可以见代码的 b 数组)


但在这道题中,依次枚举 gcd 很明显是不能行的,那么我们希望有一个方法去调优,假设当前选了 b1,b2,,bt 这样的 t 个数,按照升序排序,当前的 gcdd,我们考虑替换掉一个数,是否可以使得答案更优?

假设我们现在加入一个没有选的最小的数 ax,用它来换掉 bt,那么贡献就是

btaxd+gcd

因为元素互不相同,所以 btbt1+d,如果 ax<bt1,那么我们就有

btaxd+gcdbtbt1d+gcd>0

于是这样的调整一定是更优的。


这就意味这什么呢?

假设我们选了 t 个数,那么有原数组的前 t1 小值。

也就是说我们只需要枚举选了多少个数和最大的数选的是什么就可以了。但这样还是会 T。


考虑把 a 排序之后做一个前缀的 gcd,容易知道它一定是不断递减且成阶梯状的,段数是 O(logm) 级别的,所以你只需要枚举当前的前缀 gcd 是那个段,每一段暴力处理一遍,时间复杂度就是 O(nlogm) 的。代码


双倍经验:CF1806F1 GCD Master (easy version)


2.2 扩展欧几里得算法(exgcd)

扩展欧几里得算法,即 exgcd,旨在求不定方程 ax+by=gcd(a,b) 的一组整数解 x,y


关于一个不定方程 ax+by=c 是否有解,我们需要证明一个引理:裴蜀定理

如果 a,b 均为整数,则有整数 x,y 满足 ax+by=gcd(a,b)

证明是简单的,我们设 d=ax+by 容易发现 d 一定是 gcd(a,b) 的倍数,那么最小的 d 就是 gcd(a,b)

容易发现,如果 ax+by=cc 不是 gcd(a,b) 的倍数,这个不定方程是无解的,反之一定有解。


同时,根据 裴蜀定理 我们可以得到一个推论:

两个整数 a,b 互质,当且仅当 ax+by=1 存在解 x,y


那如何求这组方程 ax+by=gcd(a,b) 的解呢?

我们考虑欧几里得算法的递归过程 gcd(a,b)=gcd(b,amodb)

当递归结束时 b=0,我们返回 a,此时有 ax+by=gcd(a,b)x=1,y=0

所以我们可以尝试先递归去求 bx+(amodb)y=gcd(b,amodb)

那么

ax+by=gcd(a,b)=gcd(b,amodb)=bx0+(amodb)y0=bx0+(aabb)y0=b(x0aby0)+ay0x=y0,y=(x0aby0)

于是这就可以在欧几里得算法中解决了。

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,y,x);
  y-=a/b*x;return d;
}

那么我们如何从一组解推广到更多组解呢?(这样可以方便找到一组最小正整数解)

我们用 exgcd 得到了一组解 x,y,满足 ax+by=gcd(a,b)

对于 x,y 的两边我们分别 kb,+ka,容易发现 xkb,y+ka 也是一组合法解,进而,我们将 ka,kb 分别 ÷gcd(a,b) 得到的 kb/gcd(a,b),+ka/gcd(a,b) 依然是一组合法解。

所以 x 的最小正整数解是 (x%m+m)%m 其中 m=b/gcd(a,b)

通过这样的调整,我们就可以找到想要的解了。


而回到最初,如果 gcd(a,b)|c,我们把最后的答案 x,y 直接乘上 cgcd(a,b) 即可。


P1082 [NOIP2012 提高组] 同余方程

求关于 x 的同余方程 ax1(modb) 的最小正整数解。

整理一下方程,即将同余拆开,我们可以得到

ax1(modb)ax+by=1

那么下面这个东西是直接可以用 exgcd 解决的,于是你就做完啦!


那如何找到最小整数解呢?

根据上面的转化,其实就是找到最小的 xkb,于是直接用 xb 取模即可。代码


然后你就会求逆元了!


ABC340F S = 1

给你整数 XY ,它们至少满足 X0Y0 中的一个。

请找出一对满足以下所有条件的整数 (A,B) 。如果不存在这样的一对,请报告。

  • 1018A,B1018
  • xy 平面上,顶点为 (0,0),(X,Y),(A,B) 的三角形的面积为 1

1017X,Y1017

机翻的,还能看。一场非常近的 ABC,有些人在场上。


考虑如何计算这三个点组成的三角形面积呢?

用四边形面积直接减去三个角上的三角形,于是可以得到

ayab2xy2(ax)(yb)2=aybx2

而由于我们只考虑了一种象限的情况,所以 S=|aybx2|

题目要求 |aybx2|=1,则 |aybx|=2,于是可以直接用 exgcd 得到一组特解了。Hanghang 的代码代码


2.3 类欧几里得算法

f(a,b,c,n)=i=0nai+bc

其中 a,b,c,n 是常数,我们需要一个 O(logn) 的算法。


这个式子和《具体数学》3.5 中的例三相当像啊,也就是说当 n=c 时,这个式子是具有封闭形式的。

但是先不急,我们先把类欧几里得算法推完再来讨论这个问题。


如果 ac 或者 bc,那么我们可以通过取模以减小范围。

i=0nai+bc=i=0n(acc+amodc)i+bcc+bmodcc=acn(n+1)2+bc(n+1)+i=0n(amodc)i+bmodcc=acn(n+1)2+bc(n+1)+f(amodc,bmodc,c,n)

我们就把 ac 或者 bc 的情况转化成了 a<c,b<c 的情况了!


接下来,用一些处理和式和处理底和顶的技巧

 i=0nai+bc=i=0nj=0ai+bc11=j=0an+bc1i=0n[j<ai+bc]

m=an+bc,则

j=0m1i=0n[j<ai+bc]=j=0m1i=0n[j+1ai+bc]=j=0m1i=0n[j+1ai+bc]=j=0m1i=0n[ai>jc+cb1]=j=0m1i=0n[i>jc+cb1a]=j=0m1(njc+cb1a)=mnf(c,cb1,a,m1)

这就是一个递归的式子了,交换了 a,c 的位置,于是又可以重复上次的过程:先取模,再递归。

这也就是欧几里得算法的辗转相除过程,于是类欧几里得名字就是这样来的。


接下来我们对于类欧几里得算法进行一个推广,尝试去求另外两个和式:

g(a,b,c,n)=i=0niai+bch(a,b,c,n)=i=0nai+bc2


首先来推导 g,用上面类似的方法,先来考虑 ac 或者 bc 的情况。

g(a,b,c,n)=i=0niai+bc=i=0ni(acc+amodc)i+bcc+bmodcc=acn(n+1)(2n+1)6+bcn(n+1)2+i=0ni(amodc)i+bmodcc=acn(n+1)(2n+1)6+bcn(n+1)2+g(amodc,bmodc,c,n)

接下来考虑 a<c,b<c 的情况,和上面 f 较为类似.

g(a,b,c,n)= i=0niai+bc=j=0an+bc1i=0n[i>jc+cb1a]i

t=jc+cb1a,m=an+bc,那么

=j=0m1i=0n[i>t]i=j=0m1(n+t+1)(nt)2=12(mn(n+1)j=0m1t2j=0m1t)=12(mn(n+1)h(c,cb1,a,m1)f(c,cb1,a,m1))


接着,我们推导 h,方法也是类似的。

考虑 ac 或者 bc 的情况。

h(a,b,c,n)=i=0nai+bc2=i=0n(acc+amodc)i+bcc+bmodcc2=i=0n(aci+bc+(amodc)i+bmodcc)2=i=0n(aci)2+i=0nbc2+2i=0nacbci+2i=0n(aci+bc)(amodc)i+bmodcc+i=0n(amodc)i+bmodcc2=ac2n(n+1)(2n+1)6+bc2(n+1)+acbcn(n+1)+2acg(amodc,bmodc,c,n)+2bcf(amodc,bmodc,c,n)+h(amodc,bmodc,c,n)

考虑 a<c,b<c,但是平方并不好处理,于是我们对其进行一个转化

n2=2n(n+1)2n=2i=0nin

那么

h(a,b,c,n)=i=0nai+bc2=2i=0n(j=0ai+bcj)f(a,b,c,n)

还是设 m=an+bc,t=jc+cb1a,则对前面的进行求和

i=0n(j=0ai+bcj)=i=0nj=0ai+bc1(j+1)=j=0m1(j+1)i=0n[j<ai+bc]=j=0m1(j+1)i=0n[i>t]=j=0m1(j+1)(nt)=nm(m+1)2j=0m1(j+1)t=nm(m+1)2g(c,cb1,a,m1)f(c,cb1,a,m1)

三个函数一起进行递归,于是你就可以通过 P5170 【模板】类欧几里得算法 啦!代码

const ll mod=998244353,i2=499122177,i6=166374059;
ll T,a,b,c,n;

struct node{
  node () {f=g=h=0;}
  ll f,g,h;
}ans;

node calc(ll a,ll b,ll c,ll n){
  ll ac=a/c,bc=b/c,m=(a*n+b)/c,in=n*(n+1)%mod*(2*n+1)%mod*i6%mod;
  node res;
  if(a==0){
  	res.f=bc*(n+1)%mod;
  	res.g=bc*n%mod*(n+1)%mod*i2%mod;
  	res.h=bc*bc%mod*(n+1)%mod;
  	return res;
  }
  if(a>=c||b>=c){
  	res.f=n*(n+1)%mod*i2%mod*ac%mod+(n+1)*bc%mod;res.f%=mod;
  	res.g=in*ac%mod+n*(n+1)%mod*i2%mod*bc%mod;res.g%=mod;
  	res.h=ac*ac%mod*in%mod+bc*bc%mod*(n+1)%mod+ac*bc%mod*n%mod*(n+1)%mod;res.h%=mod;
  	node e=calc(a%c,b%c,c,n);
  	res.h+=e.h+2*bc%mod*e.f%mod+2*ac%mod*e.g%mod;
  	res.g+=e.g;res.f+=e.f;
  	res.h%=mod;res.f%=mod;res.g%=mod;
  	return res;
  }
  node e=calc(c,c-b-1,a,m-1);
  res.f=(n*m%mod-e.f+mod)%mod;
  res.g=(m*n%mod*(n+1)%mod-e.h-e.f+mod+mod)%mod*i2%mod;
  res.h=(n*m%mod*(m+1)%mod-2*e.g%mod-2*e.f%mod-res.f%mod+3*mod)%mod;
  return res;
}

P5179 Fraction

给你四个正整数 a,b,c,d,求一个最简分数 pq 满足 ab<pq<cd

若有多组解,输出 q 最小的一组,若仍有多组解,输出 p 最小的一组。

a,b,c,d109

一道用到类欧类似思想的题目。


什么时候我们可以不加思考地直接算出这个目标分数?

发现当 ab<1,cd>1,我们直接返回 11 就可以了,那么现在我们考虑如何进行转化。

举一个例子:

19117<pq<1485

对其取倒数,可以得到

11719>qp>8514

把两边 1 的部分换掉

114<q6pp<319

我们把这两边的分数变成更小的真分数,继续循环:

13<p6(q6p)q6p<81

从而一步一步带回即可。代码


这道题中我们把假分数转成真分数的操作和类欧类似。


ABC283Ex Popcount Sum

T 组询问,每组求 1n 内, modmr 的数在二进制下 1 的个数的和。

1T105,1mn109

x 在二进制下 1 的个数进行简单转化,代码中常用 (x>>i)&1 那么转化成数学表达式就是

i=0log2x(x2i2x2i+1)

而由于我们只统计 mj+r,于是整个式子其实就是

j=0nrmi=0log2mj+r(mj+r2i2mj+r2i+1)=i=0log2nj=0nrm(mj+r2i2mj+r2i+1)

这个东西就是容易用类欧几里得算法在 O(Tlog2n) 的时间复杂度内求出了。代码


*当 n=c1 时的封闭形式

我们来尝试求

k=0m1nk+xm,m>0n

的封闭形式,也就是 fn=c 的情况。这是有趣的,可以教给我们一些其他的东西。


前置知识:来自《具体数学》3.25 和 3.26。

n=nm+n+1m++n+m1m

如何证明这个东西?

你可以看成把 n 个物品分成 m 组,那么一定有一些组是 nm 个物品,另外一些就是 nm 个。

而从小到大排好序后,第 i 组刚好有 n+i1m 个元素。


同时,我们还可以对这个东西进行推广,用 mx 替换 n,就可以得到一个对所有实数 x 都成立的恒等式:

mx=x+x+1m++x+m1m


首先可以考虑打表,或许你可以发现一些东西,具体可以去看《具体数学》3.5。

而推导时我们可以用刚才推导类欧几里得类似的方法:对 nk 取模。

0k<mnk+xm=0k<mnkmm+nkmodm+xm

把它拆成整数部分和小数部分就是

0k<m(x+nkmodmm+nkmnkmodmm)

考虑把三项分开来求和,对于第一项和第三项,我们都涉及到了 knmodm

那么证明处理这个神秘东西呢?

容易发现这是简单的,设 d=gcd(n,m),根据上面同余关系中的一些证明,则

anbn(modm)adbd(modmd)

于是它所得到的就是把按照某种次序排列的数 0,d,2d,,md 重复 d 次,在后面我们会更为细致地讨论它。

所以这个式子的第一部分的和就是

d(xm+x+dm++x+mdm)=d(x/dm/d+x/d+1m/d++x/d+m/d1m/d)=dxd

最后一步就用到了上面前置知识中的东西。


而对于第三部分,我们发现它其实就是一个等差数列重复了 d 次,所以它的和就是

d(12(0+mdm)×md)=md2

中间的第二部分,也是非常简单的,同样是一个等差数列,它的和就是

m12n

于是我们要求的封闭形式就找出来了

0k<mnk+xm=dxd+m12n+dm2

如果对封闭形式稍作处理,就可以让它关于 m,n 对称,变成

dxd+(m1)(n1)2+d12

就得到这样的互反律

0k<mnk+xm=0k<nmk+xn

类欧几里得算法,完。


3 同余关系

在前置知识中提到了同余关系的相关法则,那么在这里我们希望对它的理解更为深刻一些。


3.1 费马小定理

费马小定理:

np11(modp),p

我们知道,p1 个数 nmodp,2nmodp,,(p1)nmodp 就是数 1,2,,p1(按照某种次序排列),于是我们把他们乘在一起就是

n×(2n)××((p1)n)(nmodp)×(2nmodp)××((p1)nmodp)(p1)!

这个式子是在 modp 的意义下成立的,所以我们可以得到

np1(p1)!(p1)!(modp)

由于 (p1)!p 是互素的,所以两边可以同时 ÷(p1)!,证毕。


同时,费马小定理还有一个更通用的表达方式

npn(modp),n


3.2 逆元

如果一个线性同余方程 ax1(modb) 有解,则乘 xamodb 的逆元,记作 a1


容易发现,这个东西可以用上面我们讨论过的 exgcd 直接求解,同时当 b 是质数时,我们也可以直接用 费马小定理 直接快速幂解决。

这样两种方法都可以做到单次求逆元 O(logn) 的时间复杂度。

而根据前面我们对 exgcd 是否有解的讨论,容易发现 a 存在逆元的充要条件是 ab


可是有些时候每次都单独求逆元并不能满足我们所需要的时间复杂度,所以这里就有了另外两种求多个数逆元的方法:

  • 线性求逆元

    首先我们知道 11=1(modp),我们尝试线性推出 i1

    那么令 k=pi,j=pmodi,于是 p=ki+jki+j0(modp)

    我们将式子两边同时乘上 i1j1 可以得到:

    kj1+i10(modp)i1kj1(modp)i1pi(pmodi)1(modp)

    这样就可以做到线性递推求逆元了。时间复杂度 O(n),就可以通过 P3811 【模板】模意义下的乘法逆元 啦!

  • 线性求 n 个数 ai 的逆元:利用前缀积求逆元

    sa 的前缀积,那么如果我们知道了 sn 的逆元,于是可以从 n 倒推回去:si1=si+11×ai

    这样每个数的逆元就是 ai1=si1×si1,时间复杂度 O(n+logp),这种方法在求阶乘的逆元中常常用到。


3.3 二次探测定理

二次探测定理:

如果 p 是一个奇素数,则方程 x21(modp) 仅有两个解:x=1x=p1


我们先不急着讨论这个东西,先来讨论 x21(modm) 有多少个解?


首先我们应该考虑 m 是素数幂 pk 的情形,这里 k>0,此时同余式 x21 可以写成

(x1)(x+1)0(modpk)

所以 p 必定整除 x1 或者 x+1,或者整除它们两者。但是 p 不可能同时整除 x1x+1,除非 p=2,如果 p>2,那么 pk(x1)(x+1)pk(x1)pk(x+1),所以恰好有两个解 x1x1

p=2 的情形稍有不同。如果 2k(x1)(x+1),那么 x1x+1 中的一个能被 2 整除,但不能被 4 整除,所以另外一个必定能被 2k1 整除。

这就意味着当 k3 时我们有四个解,即 x±1x2k1±1


现在,x21(modm) 当且仅当对 m 的完全分解式中所有满足 mp>0 的素数 p 都有 x21(modpmp)

每个素数都是独立于其他素数的,对于 xmodpmp,除了 p=2 的情形,皆有两种可能性。

于是如果 mr 个不同的素因子,则 x21 的解的总数是 2r,除了当 m 是偶数时要做修正之外,一般来说,精确的解数是

2r+[8m]+[4m][2m]


在这个推导过程中,我们已经证明了 二次探测定理 了!


3.4 威尔逊定理

接下来引出一个逆元的应用——威尔逊定理,他会在之后有关素数的判定中有所应用。

(n1)!1(modn)n,n>1

这个定理的一半是平凡的:如果 n>1 不是素数,它就有一个素因子 pp 必然也是 (n1)! 的一个因子,所以 (n1)! 不可能与 1 同余。

具体来说,在 1.4 我们引入了一个改变模数的式子,也就是如果 (n1)! 对模 n1 同余,那么就应该与模 p1 同余,但是事实并非这样。


威尔逊定理的另一半说的是,(p1)!1(modp),我们可以用 逆元配对 来证明这一半的结论。

因为 p 是素数,如果 np,那么就一定存在一个 n 满足

nn1(modp)

这里 nn 的逆元,n 也是 n 的逆元。所以如果 nn,那么它们两个就可以进行配对,从而直接抵消掉了。


那哪些数的逆元就是它本身呢?

容易发现其实就是问 x21(modp) 的解,这就是上面我们证明的二次探测定理。

p 是奇素数时,只会有 x=1x=p1,那么整个式子就是 (p1)!(p1)1(modp),证毕。

p=2 时,直接带进去也是非常显然的,这样我们就证明出来了。


3.5 有趣的遗留问题

在上面推类欧几里得算法的一个拓展时,我们希望证明 m 个数

0modm,nmodm,2nmodm,,(m1)nmodm

按照某种次序恰好组成了 md 个数

0,d,2d,,md

d 分复制,其中 d=gcd(n,m)


或许上面已经给出了一些不太严谨的简要证明,我们在这里希望把它写得更完整一些。

首先证明得到前面 md 个值的 d 份复制是简单的,根据 1.4 同余关系的一些等式,我们可以知道

jnkn(modm)j(nd)k(nd)(modmd)

从而当 0k<md 时,我们得到这些值的 d 份复制.


那么现在我们必须指出,那 md 个数就是 {0,d,2d,,md} 按照某种次数排列。

我们记 m=md,n=nd,那么根据 1.4 中的分配律就有 knmodm=d(knmodm),所以当 0k<m 时出现的那些之是 d 去乘

0modm,nmodm,2nmodm,,(m1)nmodm

而这种情形就是 mn 的情形。

根据 鸽巢原理,我们就可以轻松证明出这 m 个数就是 0,1,,m1,这个遗留问题就解决了!


它可以被理解成在一个环上面每次跳 n 步能跳到的点,在 OI 中也会有许多应用。


4 线性同余方程组

求解形如

{xa1(modn1)xa2(modn2)xak(modnk)

线性同余方程组,在 OI 中有着广泛的应用。


4.1 中国剩余定理(CRT)

中国剩余定理全称 Chinese Remainder Theorem,简称 CRT。它用于求解 模数两两互质 的线性同余方程组,即对于任意 ij,都有 ninj


首先我们来看一个例子:

在《孙子算经》中有这样一个问题:“今有物不知其数,三三数之剩二(除以 32),五五数之剩三(除以 53),七七数之剩二(除以 72),问物几何?

CRT 的步骤是这样的:

  1. 找出三个数:

    35 的公倍数中找出被 7 除余 1 的最小数 15

    37 的公倍数中找出被 5 除余 1 的最小数 21

    75 的公倍数中找出被 3 除余 1 的最小数 70

  2. 15×22 为最终除以 7 的余数),用 21×33 为最终除以 5 的余数),用 70×2

  3. 把以上三个数相加得到 233,再用它除以 3,5,7 的最小公倍数 105 ,得到余数 23 即为最小满足条件的数。


它为什么对的呢?

我们假设 m1 是一个 2(mod3) 的数, m23(mod5) 的数, m32(mod7) 的数。

m1 的角度出发,已知 m12(mod3),如果需要 (m1+m2+m3)2(mod3) ,那么 m2,m3 都是 3 的倍数。

同理我们可以得到:

  1. m1 除以 32,且是 57 的公倍数。
  2. m2 除以 53,且是 37 的公倍数。
  3. m3 除以 72,且是 35 的公倍数。

所以 CRT 用数学表示出来就是:

N=n1×n2××nkNi 表示 N÷ni

x(a1N1N11+a2N2N21++akNkNK1)(modN)

因为 NiNi1Ni1 是关于 modni 的逆元) 就满足了是其他数的公倍数,且 1(modni) ,于是再用 ai 乘上它即可。


P1495 【模板】中国剩余定理(CRT)代码

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

const int N=15;
int n;
ll p,a[N],b[N],m,x,y,ans=0;

void exgcd(ll a,ll b ,ll &x,ll &y){
  if(b==0){x=1,y=0;return;}
  exgcd(b,a%b,y,x);y-=a/b*x;
}

int main(){
  scanf("%d",&n);m=1ll;
  for(int i=1;i<=n;i++){
  	scanf("%lld%lld",&a[i],&b[i]);
  	m*=a[i];
  }
  for(int i=1;i<=n;i++){
  	p=m/a[i];
    exgcd(p,a[i],x,y);
    ans+=b[i]*p*(x<0?x+a[i]:x);
  }
  printf("%lld\n",ans%m);
  return 0;
}

4.2 扩展中国剩余定理(exCRT)

如果就是单纯的 线性同余方程组 怎么解呢?也就是 n 并不两两互质。

这就是 exCRT 所解决的问题,它能解决的东西更为普遍,故使用范围也会更广。


先考虑只有两个方程的简单情况。

{xa1(modn1)xa2(modn2)

容易发现 x=a1+n1k1=a2+n2k2,我们把右边的两个式子提出来并进行移项

n1k1n2k2=a2a1

那么这就是一个可以用 exgcd 求解的方程了。


如果 gcd(n1,n2)a2a1,那么线性同余方程组无解。

否则,我们可以得到新的方程,也就是合并了两个方程:

a=a1+n1k1,n=lcm(n1,n2)

推广到 n 个方程的情况,这样依次合并下去,最后的答案就是合并出来的 a 了。

我们就成功求解了 线性同余方程组


P4777 【模板】扩展中国剩余定理(EXCRT)代码

ll mul(ll a,ll b,ll m){
  ll res=0ll;
  while(b){if(b&1) res=(res+a)%m;a=(a+a)%m;b>>=1;}
  return res;
} 

ll exgcd(ll a,ll b, ll &x,ll &y){
  if(b==0){x=1,y=0;return a;}
  ll d=exgcd(b,a%b,y,x);y-=(a/b)*x;return d;
}

ll wrk(){
  ll ans=a[1],M=b[1];
  for(int i=2;i<=n;i++){
    ll A=M,B=b[i],C=(a[i]-ans%B+B)%B;
    ll gcd=exgcd(A,B,x,y),bg=B/gcd;
    if(C%gcd!=0) return -1;
    x=mul(x,C/gcd,bg);
    ans+=x*M;M*=bg;
    ans=(ans%M+M)%M;  	
  }
  return (ans%M+M)%M;
}

本文作者:H_W_Y

本文链接:https://www.cnblogs.com/H-W-Y/p/18203501/NumberTheory1

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

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