整除理论与线性丢番图方程相关

一.整除的定义.

整除:对于两个自然数 a , b a,b a,b,若存在一个自然数 x x x满足 a x = b ax=b ax=b,则称 a a a整除 b b b,记为 a ∣ b a|b ab.

显然 { N , ∣ } \{N,|\} {N,}构成一个偏序集.

因数(约数):对于一个自然数 n n n,任意 a a a满足 a ∣ n a|n an都称为 n n n的因数.

倍数:对于一个自然数 n n n,任意 a a a满足 n ∣ a n|a na都称为 n n n的倍数.


二.约数函数.

约数个数函数:定义约数个数函数 τ ( n ) \tau(n) τ(n)为:
τ ( n ) = ∑ d ∣ n 1 \tau(n)=\sum_{d|n}1 τ(n)=dn1

结论 τ ( n ) \tau(n) τ(n)的数值一般是在 O ( n 1 3 ) O(n^{\frac{1}{3}}) O(n31)级别的.

约数和函数:定义约数和函数 σ ( n ) \sigma(n) σ(n)为:
σ ( n ) = ∑ d ∣ n d \sigma(n)=\sum_{d|n}d σ(n)=dnd

根据定义可以在 O ( n ) O(\sqrt{n}) O(n )的时间内求解单个 τ ( n ) \tau(n) τ(n) σ ( n ) \sigma(n) σ(n).

代码如下:

int Get_tau(int n){
  int res=0;
  for (int i=1;i*i<=n;++i)
    if (n%i==0) res+=2-(i*i==n); 
  return res;
}

LL Get_sigma(int n){
  LL res=0;
  for (int i=1;i*i<=n;++i)
    if (n%i==0) res+=i+n/i-i*(i*i==n);
  return res;
}



三.约数定理与线性筛约数函数.

约数个数定理:设唯一分解 n = ∏ i = 1 k p i c i n=\prod_{i=1}^{k}p_i^{c_i} n=i=1kpici,那么有:
τ ( n ) = ∏ i = 1 k ( c i + 1 ) \tau(n)=\prod_{i=1}^{k}(c_i+1) τ(n)=i=1k(ci+1)

约数和定理:设唯一分解 n = ∏ i = 1 k p i c i n=\prod_{i=1}^{k}p_i^{c_i} n=i=1kpici,那么有:
σ ( n ) = ∏ i = 1 k ∑ j = 0 c i p i j \sigma(n)=\prod_{i=1}^{k}\sum_{j=0}^{c_i}p_i^{j} σ(n)=i=1kj=0cipij

有了这两个定理,我们可以用线性筛在 O ( n ) O(n) O(n)的时间内筛出 [ 1 , n ] [1,n] [1,n]中所有的 τ \tau τ σ \sigma σ值.

具体来说,我们同时维护 c n t ( n ) cnt(n) cnt(n) s u m ( n ) sum(n) sum(n)分别表示 n n n的仅有最小素因子组成的约数个数和约数和.形式化的,对于唯一分解 n = ∏ i = 1 k p i c i n=\prod_{i=1}^{k}p_i^{c_i} n=i=1kpici,有:
c n t ( n ) = c 1 + 1 s u m ( n ) = ∑ j = 0 c 1 p 1 j cnt(n)=c_1+1\\ sum(n)=\sum_{j=0}^{c_1}p_1^{j} cnt(n)=c1+1sum(n)=j=0c1p1j

那么对于这四个函数,在线性筛过程中分别有:
1. p p p为素数时有:
c n t ( p ) = τ ( p ) = 2 s u m ( p ) = σ ( p ) = p + 1 cnt(p)=\tau(p)=2\\ sum(p)=\sigma(p)=p+1 cnt(p)=τ(p)=2sum(p)=σ(p)=p+1

2. p ∤ n p\nmid n pn p p p n p np np的最小素因子时有:
c n t ( n p ) = 2 τ ( n p ) = 2 τ ( n ) s u m ( n p ) = p + 1 σ ( n p ) = σ ( n ) ( p + 1 ) cnt(np)=2\\ \tau(np)=2\tau(n)\\ sum(np)=p+1\\ \sigma(np)=\sigma(n)(p+1) cnt(np)=2τ(np)=2τ(n)sum(np)=p+1σ(np)=σ(n)(p+1)

3. p ∣ n p|n pn p p p n p np np的最小素因子时有:
c n t ( n p ) = c n t ( n ) + 1 τ ( n p ) = τ ( n ) c n t ( n ) c n t ( n p ) s u m ( n p ) = s u m ( n ) p + 1 σ ( n p ) = σ ( n ) s u m ( n ) s u m ( n p ) cnt(np)=cnt(n)+1\\ \tau(np)=\frac{\tau(n)}{cnt(n)}cnt(np)\\ sum(np)=sum(n)p+1\\ \sigma(np)=\frac{\sigma(n)}{sum(n)}sum(np) cnt(np)=cnt(n)+1τ(np)=cnt(n)τ(n)cnt(np)sum(np)=sum(n)p+1σ(np)=sum(n)σ(n)sum(np)

于是我们就可以写出线性筛 τ \tau τ σ \sigma σ代码:

int n,b[N+9],pr[N+9],cp;
int cnt[N+9],tau[N+9];
LL sum[N+9],sig[N+9];

void Sieve(){
  tau[1]=1;sig[1]=1;
  for (int i=2;i<=n;++i){
  	if (!b[i]){
  	  pr[++cp]=i;
  	  cnt[i]=tau[i]=2;
  	  sum[i]=sig[i]=i+1;
  	}
  	for (int j=1;j<=cp&&i*pr[j]<=n;++j){
  	  int t=i*pr[j];
	  b[t]=1;
	  if (i%pr[j]==0){
	  	cnt[t]=cnt[i]+1;tau[t]=tau[i]/cnt[i]*cnt[t];
	  	sum[t]=sum[i]*pr[j]+1;sig[t]=sig[i]/sum[i]*sum[t];
		break;
	  }
	  cnt[t]=2;tau[t]=tau[i]<<1;
	  sum[t]=pr[j]+1;sig[t]=sig[i]*sum[t];
  	}
  }
}

时间复杂度 O ( n ) O(n) O(n).


四.调和级数.

考虑枚举 i = 1 → n i=1\rightarrow n i=1n,并枚举 i i i所有的在 [ 1 , n ] [1,n] [1,n]上的倍数,即下面这串代码:

for (int i=1;i<=n;++i)
  for (int j=1;i*j<=n;++j);

容易发现这串代码的时间复杂度为:
O ( ∑ i = 1 n n i ) = O ( n ∑ i = 1 n 1 i ) O\left(\sum_{i=1}^{n}\frac{n}{i}\right)=O\left(n\sum_{i=1}^{n}\frac{1}{i}\right) O(i=1nin)=O(ni=1ni1)

这个式子并不是很好直接估计它能跑的数据范围,考虑一下能否转化为一个我们熟知的式子?

事实上,有一个叫调和级数的东西是这样定义的:
H ( n ) = ∑ i = 1 n 1 i H(n)=\sum_{i=1}^{n}\frac{1}{i} H(n)=i=1ni1

并且这个东西有这样一个性质
H ( n ) ≈ ln ⁡ n H(n)\approx \ln n H(n)lnn

证明:
H ( n ) = 1 + 1 2 + 1 3 + 1 4 + 1 5 + 1 6 + 1 7 + 1 8 + ⋯ ⇒ 1 + 1 2 + 1 4 + 1 4 + 1 8 + 1 8 + 1 8 + 1 8 ≤ H ( n ) ≤ 1 + 1 2 + 1 2 + 1 4 + 1 4 + 1 4 + 1 4 + ⋯ ⇒ H ( n ) ≈ ln ⁡ n H(n)=1+\frac{1}{2}+\frac{1}{3}+\frac{1}{4}+\frac{1}{5}+\frac{1}{6}+\frac{1}{7}+\frac{1}{8}+\cdots\\ \Rightarrow 1+\frac{1}{2}+\frac{1}{4}+\frac{1}{4}+\frac{1}{8}+\frac{1}{8}+\frac{1}{8}+\frac{1}{8}\leq H(n)\leq 1+\frac{1}{2}+\frac{1}{2}+\frac{1}{4}+\frac{1}{4}+\frac{1}{4}+\frac{1}{4}+\cdots\\ \Rightarrow H(n)\approx \ln n H(n)=1+21+31+41+51+61+71+81+1+21+41+41+81+81+81+81H(n)1+21+21+41+41+41+41+H(n)lnn

证毕.

当然你也可以直接用积分估计:
∑ i = 1 n 1 i ≈ ∫ 1 n 1 x d x = ln ⁡ n \sum_{i=1}^{n}\frac{1}{i}\approx \int_{1}^{n}\frac{1}{x}\mathrm{d}x=\ln n i=1ni11nx1dx=lnn

同时上面的证明过程证明了调和级数是发散的.

也就是说,上面那个循环的复杂度是 O ( n H ( n ) ) = O ( n log ⁡ n ) O(nH(n))=O(n\log n) O(nH(n))=O(nlogn)的(复杂度中log的底数关系不大).


五.最大公因数与最小公倍数.

最大公约数:两个正整数 a , b a,b a,b的最大公约数定义为最大的正整数 k k k满足 k ∣ a k|a ka k ∣ b k|b kb,记为 g c d ( a , b ) = k \mathrm{gcd}(a,b)=k gcd(a,b)=k.

最小公倍数:两个正整数 a , b a,b a,b的最小公倍数定义为最小的正整数 k k k满足 a ∣ k a|k ak b ∣ k b|k bk,记为 l c m ( a , b ) = k \mathrm{lcm}(a,b)=k lcm(a,b)=k.

定理1 g c d ( a , b ) = a b l c m ( a , b ) \mathrm{gcd}(a,b)=\frac{ab}{\mathrm{lcm}(a,b)} gcd(a,b)=lcm(a,b)ab.

证明只需要考虑每一个素因子的指数并利用 min ⁡ { a , b } + max ⁡ { a , b } = a + b \min\{a,b\}+\max\{a,b\}=a+b min{a,b}+max{a,b}=a+b即可.

拓展到多个数gcd与lcm之间的关系参见容斥原理相关.


六.最大公因数的求解.

如何求解两个数 a , b a,b a,b的最大公因数?最简单粗暴的方法,枚举一个数 a a a的因数,判定是不是 b b b的因数.时间复杂度 O ( a ) O(\sqrt{a}) O(a ).

当然我们还有一个更快的方法,不过在介绍它之前先介绍一个gcd的性质.

gcd性质1 g c d ( a , b ) = g c d ( b , a    m o d    b )    ( a ≥ b ) \mathrm{gcd}(a,b)=\mathrm{gcd}(b,a\,\,mod\,\,b)\,\,(a\geq b) gcd(a,b)=gcd(b,amodb)(ab).

证明:
a = k b + r a=kb+r a=kb+r,其中 k ∈ N , r ∈ [ 0 , b ) k\in N,r\in [0,b) kN,r[0,b),问题转化为证明 g c d ( a , b ) = g c d ( b , r ) \mathrm{gcd}(a,b)=\mathrm{gcd}(b,r) gcd(a,b)=gcd(b,r).
g = g c d ( a , b ) g=\mathrm{gcd}(a,b) g=gcd(a,b),则有:
g ∣ ( k b + r ) ∧ g ∣ b ⇒ g ∣ r g|(kb+r)\wedge g|b\Rightarrow g|r g(kb+r)gbgr

g = g c d ( b , r ) g=\mathrm{gcd}(b,r) g=gcd(b,r),则有:
g ∣ b ∧ g ∣ r ⇒ g ∣ ( k b + r ) g|b\wedge g|r\Rightarrow g|(kb+r) gbgrg(kb+r)

即:
g c d ( a , b ) ∣ r ∧ g c d ( b , r ) ∣ a ⇒ g c d ( a , b ) = g c d ( b , r ) \mathrm{gcd}(a,b)|r\wedge \mathrm{gcd}(b,r)|a\Rightarrow \mathrm{gcd}(a,b)=\mathrm{gcd}(b,r) gcd(a,b)rgcd(b,r)agcd(a,b)=gcd(b,r)

证毕.

有了这个性质后,我们就可以通过这个性质的式子不断迭代得到答案了,代码如下:

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

这种方法被称为辗转相除法.

根据同余的性质,每次 a a a b b b中总有一个数的大小会减半,所以时间复杂度就是 O ( log ⁡ a + log ⁡ b ) O(\log a+\log b) O(loga+logb).

同样的,对于一个序列 a i a_i ai的gcd,我们也可以用迭代来求,时间复杂度 O ( n log ⁡ a i ) O(n\log a_i) O(nlogai).


七.另一种gcd的求解方法.

根据gcd性质1,显然下面这个式子也是成立的:
gcd ⁡ ( a , b ) = gcd ⁡ ( b , a − b ) \gcd(a,b)=\gcd(b,a-b) gcd(a,b)=gcd(b,ab)

那么对于 gcd ⁡ ( a , b ) \gcd(a,b) gcd(a,b),我们可以根据 a , b a,b a,b的奇偶性来分类讨论:
1.若 a , b a,b a,b均为偶数,则有 gcd ⁡ ( a , b ) = 2 gcd ⁡ ( a 2 , b 2 ) \gcd(a,b)=2\gcd(\frac{a}{2},\frac{b}{2}) gcd(a,b)=2gcd(2a,2b).
2.若 a a a为偶数且 b b b为奇数,则有 gcd ⁡ ( a , b ) = gcd ⁡ ( a 2 , b ) \gcd(a,b)=\gcd(\frac{a}{2},b) gcd(a,b)=gcd(2a,b).
3.若 a a a为奇数且 b b b为偶数,则有 gcd ⁡ ( a , b ) = gcd ⁡ ( a , b 2 ) \gcd(a,b)=\gcd(a,\frac{b}{2}) gcd(a,b)=gcd(a,2b).
4.若 a , b a,b a,b均为奇数,则有 gcd ⁡ ( a , b ) = gcd ⁡ ( a − b , a ) \gcd(a,b)=\gcd(a-b,a) gcd(a,b)=gcd(ab,a).

容易发现时间复杂度仍然是 O ( log ⁡ a + log ⁡ b ) O(\log a+\log b) O(loga+logb)的.

代码如下:

int gcd(int a,int b){
  if (a<b) swap(a,b);
  if (!b) return a;
  if (a&1&&b&1) return gcd(a-b,b);
  if (a&1) return gcd(a,b>>1);
  if (b&1) return gcd(a>>1,b);
  return gcd(a>>1,b>>1)<<1;
}

这个算法的好处是它不需要做除法,如果 a , b a,b a,b都需要用高精度存时,这个算法会有十分明显的优势.


八.gcd的一些性质.

gcd性质2:假设 a 0 = 0 a_0=0 a0=0,那么有:
g c d i = 1 n { a i } = g c d i = 1 n { a i − a i − 1 } \mathrm{gcd}_{i=1}^{n}\{a_i\}=\mathrm{gcd}_{i=1}^{n}\{a_i-a_{i-1}\} gcdi=1n{ai}=gcdi=1n{aiai1}

gcd性质3:假设 a 0 = + ∞ a_0=+\infty a0=+,那么有:
g c d i = 1 n { a i } = g c d i = 1 n { a i    m o d    a i − 1 } \mathrm{gcd}_{i=1}^{n}\{a_i\}=\mathrm{gcd}_{i=1}^{n}\{a_i\,\,mod\,\,a_{i-1}\} gcdi=1n{ai}=gcdi=1n{aimodai1}

gcd性质4:对于任意一个正整数数列 a i a_i ai,定义 g i = g c d j = 1 i { a j } g_i=\mathrm{gcd}_{j=1}^{i}\{a_j\} gi=gcdj=1i{aj},那么有:
g i ≠ g i + 1 ⇒ 2 g i + 1 ≤ g i g_i\neq g_{i+1}\Rightarrow 2g_{i+1}\leq g_i gi=gi+12gi+1gi



九.丢番图方程.

丢番图方程:定义一个方程 ∑ i = 1 n a i x i b i = c \sum_{i=1}^{n}a_ix_i^{b_i}=c i=1naixibi=c为丢番图方程,当且仅当任意 a i , c ∈ Z , b i ∈ N + a_i,c\in Z,b_i\in N_+ ai,cZ,biN+ a i ≠ 0 a_i\neq 0 ai=0,并且我们只讨论丢番图方程的整数解.

线性丢番图方程:满足所有 b i = 1 b_i=1 bi=1的丢番图方程称为线性丢番图方程.

接下来我们主要研究线性丢番图方程.

二元线性丢番图方程:满足 n = 2 n=2 n=2的线性丢番图方程称为二元线性丢番图的方程.


十.二元线性丢番图方程的求解.

裴蜀定理:二元线性丢番图方程 a x + b y = gcd ⁡ ( a , b ) ax+by=\gcd(a,b) ax+by=gcd(a,b)必然存在一组整数解.

证明:
首先对于 b = 0 b=0 b=0的情况,必然存在整数解 x = 1 , y = 0 x=1,y=0 x=1,y=0.
那么对于任意一个方程 a x + b y = gcd ⁡ ( a , b ) ax+by=\gcd(a,b) ax+by=gcd(a,b),必然可以构造出另一个方程:
b x ′ + ( a    m o d    b ) y ′ = gcd ⁡ ( b , a    m o d    b ) = gcd ⁡ ( a , b ) b x ′ + ( a − ⌊ a b ⌋ b ) y ′ = gcd ⁡ ( a , b ) b x ′ + a y ′ − b ⌊ a b ⌋ y ′ = gcd ⁡ ( a , b ) a y ′ + b ( x ′ − ⌊ a b ⌋ y ′ ) = gcd ⁡ ( a , b ) bx'+(a\,\,mod\,\,b)y'=\gcd(b,a\,\,mod\,\,b)=\gcd(a,b)\\ bx'+\left(a-\left\lfloor\frac{a}{b}\right\rfloor b\right)y'=\gcd(a,b)\\ bx'+ay'-b\left\lfloor\frac{a}{b}\right\rfloor y'=\gcd(a,b)\\ ay'+b\left(x'-\left\lfloor\frac{a}{b}\right\rfloor y'\right)=\gcd(a,b) bx+(amodb)y=gcd(b,amodb)=gcd(a,b)bx+(abab)y=gcd(a,b)bx+aybbay=gcd(a,b)ay+b(xbay)=gcd(a,b)

也就是说,只要方程 b x ′ + ( a    m o d    b ) y ′ = gcd ⁡ ( b , a    m o d    b ) bx'+(a\,\,mod\,\,b)y'=\gcd(b,a\,\,mod\,\,b) bx+(amodb)y=gcd(b,amodb)有解,必然可以构造方程 a x + b y = gcd ⁡ ( a , b ) ax+by=\gcd(a,b) ax+by=gcd(a,b)的一组解:
x = y ′ y = x ′ − ⌊ a b ⌋ y ′ x=y'\\ y=x'-\left\lfloor\frac{a}{b}\right\rfloor y' x=yy=xbay

根据数学归纳法即可证明.
证毕.

裴蜀定理的推论:二元线性丢番图方程 a x + b y = c ax+by=c ax+by=c存在整数解当且仅当 gcd ⁡ ( a , b ) ∣ c \gcd(a,b)|c gcd(a,b)c.

证明:
根据裴蜀定理可以证明充分性.
而我们给等式两边除掉 gcd ⁡ ( a , b ) \gcd(a,b) gcd(a,b)后,等式左边仍然为整数,此时等式右边也必须为整数,所有需要 gcd ⁡ ( a , b ) ∣ c \gcd(a,b)|c gcd(a,b)c,必要性得证.
证毕.

根据证明过程,我们还可以写出求一组解的求解算法,称之为扩展欧几里得算法.

代码如下(注意这个代码只能求解 a , b a,b a,b为正的情况,若需要为负可以先把符号交给 x , y x,y x,y):

int Ex_gcd(int a,int b,int &x,int &y){
  if (!b) {x=1;y=0;return a;}
  int g=Ex_gcd(b,a%b,y,x);
  y-=a/b*x;
  return g;
}

时间复杂度与求解gcd的时间复杂度相同,为 O ( log ⁡ a + log ⁡ b ) O(\log a+\log b) O(loga+logb).

这个算法得到的解一般不会太大,不至于到爆long long的地步.

同时,可以十分容易的证明,若一个二元线性丢番图方程 a x + b y = c ax+by=c ax+by=c有一组解 x 0 , y 0 x_0,y_0 x0,y0,则此二元线性丢番图方程的通解为:
x = x 0 + k b gcd ⁡ ( a , b ) y = y 0 − k a gcd ⁡ ( a , b ) x=x_0+k\frac{b}{\gcd(a,b)}\\ y=y_0-k\frac{a}{\gcd(a,b)} x=x0+kgcd(a,b)by=y0kgcd(a,b)a

其中 k k k是任意整数.


十一.线性丢番图方程的求解.

对于一般线性丢番图方程:
∑ i = 1 n a i x i = c \sum_{i=1}^{n}a_ix_i=c i=1naixi=c

一般线性丢番图方程解存在性判定法则:若 g c d i = 1 n { a i } ∣ c \mathrm{gcd}_{i=1}^{n}\{a_i\}|c gcdi=1n{ai}c则有整数解,否则无整数解.

同时,对于线性丢番图方程的求解,可以考虑先求出分 n − 1 n-1 n1步求解,第 i i i步求解出方程:
g c d j = 1 i { a j } x + a i + 1 x i + 1 = g c d j = 1 i + 1 { a j } \mathrm{gcd}_{j=1}^{i}\{a_j\}x+a_{i+1}x_{i+1}=\mathrm{gcd}_{j=1}^{i+1}\{a_j\} gcdj=1i{aj}x+ai+1xi+1=gcdj=1i+1{aj}

时间复杂度 O ( n log ⁡ a i ) O(n\log a_i) O(nlogai).

代码如下(注意这个代码只能求解 a i a_i ai为正的情况,而且这个算法得到解的数值没有上限保证,可能会爆long long之类的):

int Ex_gcd(int a,int b,int &x,int &y){
  if (!b) {x=1;y=0;return a;}
  int g=Ex_gcd(b,a%b,y,x);
  y-=a/b*x;
  return g;
}

int t[N+9];

bool Get_root(int *a,int n,int c,int *res){
  int g=a[1];
  res[1]=t[n]=1;
  for (int i=2;i<=n;++i) g=Ex_gcd(g,a[i],t[i-1],res[i]);
  if (c%g) return 0;
  c/=g;
  for (int i=n-1;i>=1;--i) res[i]*=t[i]*=t[i+1];
  for (int i=1;i<=n;++i) res[i]*=c;
  return 1;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值