数论(长期更新)

欧几里得算法

gcd直接辗转相除法更相损减术即可。前者是数论题中常见的,后者常见于数据结构题中转化到差分序列上的做法。

int gcd(int a,int b){
    if(!b) return a;
    return gcd(b,a%b);
}

O(logn)

更相损减术与之类似(假如真的要写的话)。

exgcd

求解不定方程ax+by=c的一组特解。

裴蜀定理

a,b s.t. (a0b0)a,bZ,x,yZ,gcd(a,b)ax+by,x,yZ s.t. ax+by=gcd(a,b)

逆定理:

a,b是不全为0的整数,若d>0a,b的公因数,且x,yZ s.t. ax+by=d,则d=gcd(a,b)

可推广到n个整数的形式。

对于解方程来说,有解gcd(a,b)c

找特解/通解

类似辗转相除,最后找到一组特解后回代,得到原来方程的特解。

通解形式也很好找。

x=x0+sbgcd(a,b)

y=y0+sagcd(a,b)

其中sZ

luogu 板子

主要是一堆分讨和细节。

看代码:

板子
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
LL t,a,b,c,x,y,x1,yy,dx,dy;
LL gcd(LL a,LL b)
{
	if(!b) return a;
	return gcd(b,a%b);
}

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

int main()
{
	scanf("%lld",&t);
	while(t--)
	{
		scanf("%lld%lld%lld",&a,&b,&c);
		LL d=gcd(a,b);
		if(c%d)
		{
			printf("-1\n");
		}
		else 
		{
			exgcd(a,b,x,y);
			x=c/d*x;
			y=c/d*y;
			dx=b/d,dy=a/d;
			LL k;
			if(x<0) k=ceil((1.0-x)/dx),x+=dx*k,y-=dy*k;
			if(x>=0) k=(x-1)/dx,x-=dx*k,y+=dy*k;
			if(y>0)
			{
				printf("%lld ",(y-1)/dy+1);
				printf("%lld ",x);
				printf("%lld ",(y-1)%dy+1);
				printf("%lld ",(y-1)/dy*dx+x);
				printf("%lld\n",y);
			}
			else
			{
				printf("%lld ",x);
				printf("%lld\n",y+(LL)ceil((1.0-y)/dy)*dy);
			}
		}
	}
	return 0;
}

类欧几里得算法

给定n,a,b,c,求i=0nai+bci=0niai+bc以及i=0nai+bc2

设:

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

类欧的主要想法就是把式子中的aamodc,然后递归下去算。

首先考虑算f(a,b,c,n)

ac或者bc时,可以让a,bc取模:

f(a,b,c,n)=i=0nai+bc=i=0n(cac+amodc)i+cbc+bmodcc=i=0naci+bc+(amodc)i+bmodcc=acn(n+1)2+(n+1)bc+f(amodc,bmodc,c,n)

然后只需算a<cb<c的情况,此时我们可以将整个向下取整的式子换掉:

f(a,b,c,n)=i=0nai+bc=i=0nj=0ai+bc11=j=0an+bc1i=0n[j<ai+bc]

对于后面括号中的式子,进行等价的变形:

j<ai+bcj+1ai+bcj+1ai+bccj+cbaicj+cb1<aicj+cb1a<icj+cb1a<i

那么原来的式子就是:

f(a,b,c,n)=j=0an+bc1i=0n[cj+cb1a<i]=j=0an+bc1ncj+cb1a=nan+bcj=0an+bc1cj+cb1a=nan+bcf(c,cb1,a,an+bc1)

这样递归即可,这里a,c位置互换,结合之前的取模,时间复杂度是O(logmax(a,c))的。

那么f就算好了。h,g几乎一致,但还是有点不同,来推一下:

来算g

ac或者bc时:

g(a,b,c,n)=i=0niai+bc=i=0ni(cac+amodc)i+cbc+bmodcc=i=0naci2+bci+i(amodc)i+bmodcc=n(n+1)(2n+1)6ac+n(n+1)2bc+g(amodc,bmodc,c,n)

a<cb<c时:

g(a,b,c,n)=i=0niai+bc=i=0nj=0ai+bc1i=j=0an+bc1i=0ni[j<ai+bc]=j=0an+bc1i=0ni[cj+cb1a<i]

为方便,以下令m=an+bc,t=cj+cb1a

g(a,b,c,n)=j=0m1i=0ni[t<i]=j=0m1n(n+1)2t2+t2=12(mn(n+1)j=0m1cj+cb1a2+j=0m1cj+cb1a)=12(mn(n+1)h(c,cb1,a,m1)+f(c,cb1,a,m1))

g就算好了,但是发现需要h。那来算h

ac或者bc时:

h(a,b,c,n)=i=0nai+bc2=i=0n(aci+bc+(amodc)i+bmodcc)2=i=0n(ac2i2+bc2+(amodc)i+bmodcc2+2acbci+2aci(amodc)i+bmodcc+2bc(amodc)i+bmodcc)=n(n+1)(2n+1)6ac2+(n+1)bc2+h(amodc,bmodc,c,n)+n(n+1)acbc+2acg(amodc,bmodc,c,n)+2bcf(amodc,bmodc,c,n)

a<cb<c时:

h(a,b,c,n)=i=0nai+bc2

n2写成(2i=0ni)n的形式:

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

综上,f,g,h在计算时相互调用即可,或者使用一个结构体同时算出三者。

边界就是n<0或者a=0

数论分块

用于和式计算中将含有ni的项在其值相同的时候打包计算。

可以发现ni的取值只有O(n)种:对于1in,只有ni;对于n<inni显然至多有n种取值。

主要记忆一些结论:

  • 使得ni=nj成立的最大的j=nniO(n)

  • 使得ni=nj成立的最大的j=n1n1i,注意i=n时特殊处理,否则分母为0O(n)

  • 二维/高维形式上,每一维都和一维形式相同,故取min即可。

  • 使得np=nq成立的最大的q=nnp2

数论函数

积性函数

若对于abf(ab)=f(a)f(b),则称f是一个积性函数。

若对于任意的a,bf(ab)=f(a)f(b),则称f是一个完全积性函数。

一般来说,对于积性函数f(n)f(1)=1

一些常见的积性函数:

  • 单位函数:ϵ(n)=[n=1],同时是完全积性。

  • 恒等函数:idk(n)=nk,同时是完全积性。当k=1时,简记为id(n)=n

  • 常数函数:1(n)=1,同时是完全积性。

  • 除数函数:σk(n)=d|ndkσ0(n)即因数个数,简记为d(n)σ1(n)即因数之和,简记为σ(n)

  • 欧拉函数。

  • 莫比乌斯函数。

积性函数的取值由素数幂处的取值决定,于是求积性函数时总是关注其素数幂处的取值。

狄利克雷卷积(Dirichlet卷积)

h(n)=d|nf(d)g(nd)

hfg的Dirichlet卷积,记作h=fg

和狄利克雷生成函数相关,对应生成函数的乘法。

上面的莫比乌斯反演式子就可以写成:f=g1g=fμ

这是由莫比乌斯函数的性质得到的:μ1=ϵ

欧拉函数的性质可以表示为:φ1=id

狄利克雷卷积的性质:

  • 交换律,fg=gf

  • 结合律,f(gh)=(fg)h

  • 分配律,f(g+h)=fg+fh

  • 等式的性质:f=gfh=gh

  • 单位元:fϵ=f

  • 逆元:fg=ϵ,则f,g互为逆元。逆元是唯一的。

重要结论:

  1. 两个积性函数的卷积还是积性函数。这个可以证明一坨和式是积性的,然后就可以去筛它。

  2. 一个积性函数的逆还是积性函数。

点乘

定义点乘(AB)(n)=A(n)B(n)

性质:当C完全积性函数时(AC)(BC)=(AB)C。即此时点乘对狄雷克利卷积有分配律。

C=idk很常见。

常用的一些东西:

  • (μidk)idk=(μidk)(1idk)=(μ1)idk=ϵidk=ϵ

  • (φidk)idk=(φidk)(1idk)=(φ1)idk=idk+1

可以发现两个积性函数的点乘还是积性函数。

欧拉函数

φ(n)定义为n的正整数中有多少个与n互质。

φ(n)=i=1n[gcd(i,n)=1]

运用下面的μ的性质,可以化简定义式:

φ(n)=i=1n[gcd(i,n)=1]=i=1nd|gcd(i,n)μ(d)=d|nμ(d)id(nd)

由两个积性函数的狄利克雷卷积还是积性函数,可知φ的积性。

来看其素数幂处的取值,设pPrime,则φ(p)=p1,φ(pk)=pkpk1

那么可以得到单点求φ(n)的式子,以下设n=i1,piPrimepiki

φ(n)=piPrimeφ(piki)=pipiki(11pi)=npipi1pi

还有常用的性质:n=d|nφ(n)

直接用μid=φ两边卷上1即可得到。可以用于替换一坨式子,或许可以称之欧拉反演(?

莫比乌斯函数

μ(x)={1x=10x含平方因子(1)kkx的不同质因子个数

初始化时记得μ(1)=1

莫比乌斯反演

性质:

ϵ(n)=[n=1]=dnμ(d)

证明一下:

显然dnμ(d)=dnμ(d),其中设n=1kxpkak,那么n=1kxpk

于是结合定义,原式即0kx(1)k(xk)=[x=0]=[n=1]=ϵ(n)

反演式子:f(n)=d|ng(d)g(n)=d|nμ(d)f(nd)

筛法

埃氏筛

考虑筛素数。一个方法是从2开始,把一个数字的所有倍数都划去,剩下的数就是素数了。O(nloglogn)

线性筛(欧拉筛)

埃氏筛还是太慢,关注其过程,发现一个数会被它的每个质因子都标记一次。

我们优化这一点,让一个数只会被它的最小质因子标记。

这样是O(n)的。

实现是为人所熟知的。

实现
void init(){
	np[0]=np[1]=true;
	for(int i=2;i<=lim;++i){
		if(!np[i]) p.emplace_back(i);
		for(int j=0;j<(int)p.size()&&p[j]*i<=lim;++j){
			np[i*p[j]]=true;
			if(i%p[j]==0) break;
		}
	}
}

注意到在线性筛的过程中,我们也得到了一个数i的最小质因子。

筛积性函数

对于当前枚举的素数p和合数(也可能是质数,但是这里当做合数)i,我们只需考虑两种情况。

  • ip,那么由积性函数性质,f(ip)=f(i)f(p)。这里的f(i)f(p)都是已经算过的。

  • p|i,那么这时pi的最小质因子,这里就要特殊考虑转移了,需要一些精细的观察。

  • 特别地,当筛出了一个素数p时,f(p)的取值要特殊算。

还有一种对注意力要求不是很高的方法:

我们设n=i=1mpiαig(n)=p1α1

那么g(n)是可以在线性筛过程中轻松算出来的。

f(n)=f(ng(n))f(g(n))。注意特判g(n)=n的情况。

这里要求对于任意素数p,能够快速求出f(pk)

杜教筛

这里不需要积性的良好性质。

对于任意数论函数f(n),求S(n)=i=1nf(i)

先找到另一个数论函数g(n),设fg=h,先来看h的前缀和:

i=1nh(i)=i=1nd|ig(d)f(id)=d=1ng(d)i=1ndf(i)=d=1ng(d)S(nd)

把右边第一项拿出来,得到g(1)S(n)=i=1nh(i)d=2ng(d)S(nd),注意右侧下标从2开始。

如果g,h的前缀和好求,那么右边就可以整除分块做了。但是直接做是O(n34)的。通过分析复杂度加均值不等式,可以得到用线性筛预处理前m=n23S(i)时,复杂度为O(n23)

PN筛

Powerful Number

设正整数n=i=1mpiki,如果对于每一个1imki>1,则称n是一个Powerful Number。

性质:

首先,每一个PN都可以表示为a2b3的形式。这是显然的,考虑每一个指数,如果它是偶数,全部扔到a2中,否则拿出3次扔到b中,剩下的扔到a2中。

然后,我们可以证明n以内的PN有O(n)个。先枚举a,然后看合法的b有多少个。1nnx23dx=O(n)

如何求n以内的所有PN呢?线性筛出n以内的所有素数,然后DFS各素数的指数即可。O(n)

Powerful Number筛

即PN筛。

需要积性

求特定积性函数前缀和:S(n)=i=1nf(i)

PN筛要求存在一个函数g满足:

  • g积性函数

  • g的前缀和容易求。

  • 在质数p处,g(p)=f(p)

假设现在已经找到了g,我们设G(n)=i=1ng(i)

我们又构造h使得gh=f,由狄雷克利卷积的性质可知h的积性,于是h(1)=1

在素数p处,f(p)=g(p)h(1)+g(1)h(p)=g(p),于是h(p)=0

根据h的积性以及h(p)=0,可知h只在PN或者1处取得非零值。这里注意h(1)=1

根据f=gh,可以来求S(n)

i=1nf(i)=i=1nd|ig(d)h(id)=d=1nh(d)i=1ndg(i)=1dn,dPNh(d)G(nd)

以上注意d=1时也要计算值。

现在枚举dO(n)的。要计算h(d),考虑利用它的积性,只需计算出h(pc)处的取值即可(c>1)。

h(pc)的方法有两种:

  • 直接推式子找到h的通项。

  • 递推一下,根据f=gh可得f(pc)=i=0cg(pi)h(pci),提出第一项就有h(pc)=f(pc)i=1cg(pi)h(pci)

复杂度O(nlogn),而且很松。

费马小定理

p为素数,gcd(a,p)=1,则ap11(modp)

证明不会。

于是当(a,p)=1时,axmodp=axmod(p1)modp

欧拉定理

gcd(a,m)=1 ,则 aφ(m)1(modm)

扩展欧拉定理

ab={abmodφ(p)gcd(a,p)=1abgcd(a,p)1,b<φ(p)abmodφ(p)+φ(p)gcd(a,p)1,bφ(p)

乘法逆元

乘法逆元的n种求法。

ax1(modp)

  1. 扩欧:要求gcd(a,b)=1

  2. 费马小定理:要求p是素数且gcd(a,p)=1

  3. 线性求逆元:

首先,显然111(modp)

我们希望求i1,考虑p=ki+j(0j<i),则ki+j0(modp)

两侧同乘i1j1,则kj1+i10(modp),即i1kj1(modp)

由于负数不太好,再加上pj1,则i1(pk)j1(modp)

  1. 求任意n个数的逆元:求出前缀积的逆元,再倒着推回去求每个前缀积的逆元,那么每个数的逆元都可以O(1)计算。

同余基本性质

应该都知道吧。

中国剩余定理

{xa1(modm1)

求解一元线性同余方程组。

一般的CRT

强调m1,,mn互质。

过程:

  1. M=mi

  2. 对于第i个方程,求pi=Mmi以及pipi11(modmi)

  3. x=aipipi1modM

可以证明这样是对的。

扩展中国剩余定理(exCRT)

考虑合并两个方程xa1(modm1)xa2(modm2)

x0=m1λ1+a1=m2λ2+a2

于是用exgcd求出(λ1,λ2)或者报告无解。

然后得到通解形式x=x0+lcm(m1,m2),这个容易验证是对的。

于是合并得到了xx0(modlcm(m1,m2))

两两合并n次就解出来了。

Lucas定理和exLucas

Lucas

强调模数p为质数。

(nm)(npmp)(nmodpmmodp)(modp)其中p为质数。

证:

注意到 (pn)[n=pn=0](modp)

因此 (a+b)pap+bp(modp)

对于 f(x)=(1+x)n[xm]f(x)=(nm)

我们现在对 f(x) 做一点变换,

f(x)=(1+x)n=(1+x)p×np(1+x)nmodp=((1+x)p)np(1+x)nmodp

所以 f(x)(1+xp)np(1+x)nmodp(modp)

h(x)=(1+xp)np,g(x)=(1+x)nmodp

[xm]f(x)[xkp]h(x)×[xr]g(x)(modp)

因为 0r<p,所以将 m 拆成 m=kp+r 的形式的方法是唯一的,即 k=mp,r=mmodp

所以 [xkp]h(x)=(npmp),[xr]g(x)=(nmodpmmodp)

原式得证。

感觉不是很严谨,感性理解一下。

exLucas

和上方的Lucas定理相比,除了用途,其余并无关系。

求解:

(nm)modM

其中M为合数。

首先我们可以对M唯一分解,M=i1piki

然后使用CRT把原问题拆了:求出(nm)modpk,然后可以合并出原问题的答案。

(nm)写成阶乘形式,(nm)=n!m!(nm)!

现在还求不了逆元,因为分母可能不与pk互质。所以直接把阶乘中所有p因子给提出来。

vp(n)表示n!p因子的个数,这个是很容易递归求的,vp(n)=vp(np)+np

于是现在就是要求n!pvp(n)m!pvp(m)(nm)!pvp(nm)×pvp(n)vp(m)vp(nm)modpk

分母上的数现在可以求逆元了,那么现在要求的就是n!pvp(n)modpk

考虑把n!拆一下,将每个p的倍数都拆出来一个p因子,可以得到n!=(np)!×pnp×pi,i=1ni

再把两边都除以vp(n),得到n!pvp(n)=(np)!pvp(np)×pi,i=1ni

右边前面那一坨显然是可以递归下去做的。

后面那一坨我们再拆一下。由于modpk,那么肯定有长为pk的循环节,于是pi,i=1ni=(pi,i=1pki)npk×pi,i=1nmodpki

前后两坨都可以O(pk)算出来,前面可以套个快速幂。或者由Wilson定理,前面括号内的东西modpk只会是±1,于是关注一下指数的奇偶就行。

这样递归下去算,时间复杂度单次询问O(plogp)。或许可以预处理一些东西做到更好的复杂度。

原根

需要了解一些性质。

离散对数

主要用大步小步。

BSGS

求解axb(modm),其中am,0x<m。注意m不一定是素数。

我们可以设x=AmB,那么上式就是aAmb×aB(modm)

其中1A,Bm

我们已知a,b,可以先枚举B算出右边的所有取值,存到Hash表中,然后枚举A算左边的取值,在Hash表中查找。

从而我们可以得到所有的x=AmB

这里要求am是为了等式两边可以同时除以aB

O(m)

进阶应用

求解xab(modp)

可以转化为BSGS。

exBSGS

剩余

主要用二次剩余。

定义:令ap,若存在xZ使得x2a(modp),则称a为模p的二次剩余,否则称a为模p的二次非剩余。

p=2时是简单的,这里只讨论p为奇素数的情况。

Euler判别法

对于奇素数pap

ap12{1(modp)xZ,x2a(modp)1(modp)Otherwise.

二次剩余的数量

在模奇素数p的意义下,二次剩余和二次非剩余都有p12个。

Lengendre符号

(ap)={0pa1(pa)(xZ,ax2(modp))1Otherwise.

性质:

对于任意整数a

ap12(ap)(modp)

使用原根可以证明。

Cipolla算法

求解x2n(modp)

我们先找一个a,使得a2n是二次非剩余。由于二次非剩余占了一半,所以这是容易得到的。

根据上面的性质,(a2n)p121(modp)

我们在模p意义下定义一个虚数单位i2(a2n),将所有虚数表示为A+Bi,并认为虚数取模就是A,B分别取模。

Lemma 1:

ipi(modp)

证明:ip=i(i2)p12(a2n)p12ii(modp)

Lemma 2:

(A+B)pAp+Bp(modp)

证明:用二项式定理展开即可。

我们可以进行计算了:

(a+i)p+1(a+i)p(a+i)(ap+ip)(a+i)(ai)(a+i)a2i2n(modp)

于是得到一个解为(a+i)p+12。可以通过反证法得到这个解总是为实数。

设存在复数A+Bi(B0)满足(A+Bi)2n(modp),那么就有A2+B2(a2n)n2ABi(modp)

左边是实数,那么右边必为实数,于是A=0,所以(Bi)2n(modp),即i2nB2(modp)。左边是二次非剩余,右边是二次剩余,于是矛盾。得证。

代码实现需要一个手写复数类。

代码
#include<bits/stdc++.h>

using namespace std;

#define gc getchar
int rd(){
	int f=1,r=0;
	char ch=gc();
	while(!isdigit(ch)){ if(ch=='-') f=-1;ch=gc();}
	while(isdigit(ch)){ r=(r<<1)+(r<<3)+(ch^48);ch=gc();}
	return f*r;
}

mt19937 rnd(233);
int mo,i_2;

inline int add(int x,int y){
	return x+y>=mo?x+y-mo:x+y;
}

inline int sub(int x,int y){
	return x-y<0?x-y+mo:x-y;
}

inline int mul(int x,int y){
	return 1ll*x*y%mo;
}

struct cpl{
	int re,im;
	
	cpl(int _x=0,int _y=0):re(_x),im(_y){}
	
	cpl operator*(const cpl &tmp){
		return cpl(add(mul(re,tmp.re),mul(mul(im,tmp.im),i_2)),add(mul(re,tmp.im),mul(im,tmp.re)));
	}
};

int ksm(int x,int y){
	int rs=1;
	while(y){
		if(y&1) rs=mul(rs,x);
		x=mul(x,x);
		y>>=1;
	}
	return rs;
}

cpl ksm(cpl x,int y){
	cpl rs(1,0);
	while(y){
		if(y&1) rs=rs*x;
		x=x*x;
		y>>=1;
	}
	return rs;
}

bool lengendre(int x){
	return ksm(x,(mo-1)>>1)==1;
}

int cipolla(int n,int p){
	n%=p,mo=p;
	if(!n) return 0;
	if(!lengendre(n)) return -1;
	int a=rnd()%mo;
	while(lengendre(sub(mul(a,a),n))) a=rnd()%mo;
	i_2=sub(mul(a,a),n);
	return ksm(cpl(a,1),(mo+1)>>1).re;
}

int T,n,p;

void solve(){
	n=rd(),p=rd();
	int rs=cipolla(n,p);
	if(!rs) puts("0");
	else if(rs==-1) puts("Hola!");
	else{
		if(rs>p-rs) rs=p-rs;
		printf("%d %d\n",rs,p-rs);
	}
}

int main(){
	T=rd();
	while(T--) solve();
	return 0;
}

Miller-Rabin素性测试和Pollard Rho分解质因数算法

这是唯一真史

素数

定义都知道。

素数有无限个。欧拉有一个巧妙的证明。

素数定理

π(x)表示x的数中素数的个数。

那么π(x)xlnxxlnx是下界。π(x)略大一些。

哥德巴赫猜想

猜想:任意4的偶数,都可以表示为两个质数的和。

这个在很大的范围内都是对的,但是还没有证明。

Stern-Brocot Tree和法里级数

Stern-Brocot树

可以构造满足mn的所有非负分数mn的集合。

我们从初始的两个分数(01,10)出发,不断进行以下操作:

在两个相邻分数mnmn之间插入m+mn+n

新的分数m+mn+n称为mnmn中位分数

这样可以用一个三元组来描述一个位置:(mn,m+mn+n,mn)

这些分数的阵列可以看成是一棵无限的二叉树构造。(mn,m+mn+n,mn)的左儿子是(mn,2m+m2n+n,m+mn+n),右儿子是(m+mn+n,m+2mn+2n,mn)

它的顶端看起来像是这样:

来看它的性质。最重要的一个是:如果mnmn是这个构造中任意一个阶段的相邻分数,我们就有mnmn=1

我们来归纳证明它。初始状态(01,10)是符合的。

当插入一个中位分数时,我们要验证的就是:

(m+m)nm(n+n)=1m(n+n)(m+m)n=1

这里打开括号后是与原来的条件等价的。于是这个性质总是成立。

有这个东西后由裴蜀定理的逆定理,我们就知道了Stern-Brocot树中的分数总是最简的。

此外,设0mn<mn,容易证明mn<m+mn+n<mn

这就说明了Stern-Brocot树不会重复。

下面说明不会遗漏任意一个非负分数。

考虑ab,首先有mn<ab<mn

我们每次生成一个中间分数mn<m+mn+n<mn,从而可以类似二分的找下去。这个过程不可能无限进行,因为条件

abmn>0 和 mnab>0

蕴含

anbm1 和 bman1

从而

(m+n)(anbm)+(m+n)(bman)m+n+m+n

我们把左边打开就得到了amnbmn+bmnamn=a(mnmn)+b(mnmn)=a+bm+n+m+n

右边在不断增大,左侧不变,于是在至多a+b步后我们可以得到ab

我们可以用矩阵描述在Stern-Brocot树上的行动。

设当前点为m+mn+n,我们把它的两个祖先放到矩阵中,但是要注意上下颠倒一下,表示为[nnmm]

颠倒是为了初始时更加符合直觉:I=[1001]

设当前点的矩阵为M,我们用ML表示向左边走一步得到的矩阵,MR表示向右边走一步得到的矩阵。

那么可以得到L=[1101],R=[1011]

f(S)表示经过S中的依次移动走到的分数对应的矩阵,那么有f(S)=S

我们还可以观察到,Stern-Brocot树是一棵二叉搜索树,于是我们可以在它上面进行二叉搜索。

以上二者结合,有两种二叉搜索的方式来确定mn在Stern-Brocot树中的位置:

  • I开始移动。

  • mn开始移动回到I

我们还可以发现,移动过程中可能会连续进行很多次L或者R,我们可以二分长度,一整段L或者R一起跳过。

复杂度是log2(m+n)的,这是因为“拐弯”的位置只会有log(m+n)个,一次拐弯后分子分母都会变成原来的两倍,所以得证。

这样就可以快速二分了。

法里级数

阶为N的法里级数记作FN,它是介于01之间的分母不超过N的所有最简分数组成的集合,而且按照递增的次序排列。

这是Stern-Brocot树的一个子树。

很牛,但是整棵Stern-Brocot树还是更有意义。

posted @   RandomShuffle  阅读(6)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示