《算法竞赛进阶指南》数论篇(1)-最大公约数,素数筛,欧拉函数,同余,欧拉定理,BSGS

372 阅读7分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

下述理论主要参考书目: 电子版pdf:算法竞赛进阶指南(p133-150)

@[TOC]

首先补充两个前置知识:约数和最大约数有关性质,可选择性看~

有关素数筛的算法 埃式nlog(n)nlog(n)&欧拉素数筛o(n)o(n)&大区间素数筛

约数

在这里插入图片描述

最大公约数

1.gcd(a,b)=gcd(a,a-b)=gcd(b,a-b)=d;

2.欧几里得算法: b!=0; gcd(a,b)=gcd(b,a%b)=d;

(ps:其实这里有个一般性的结论:gcd(m,nn)=gcd(m,km+r1k*m+r_1),n=km+r1k*m+r_1,k为整数,0<=r1r_1<m)

1.证明:d|a 且 d|b 那么d| (a-b) 得证

2.证明:a<b 显然易得 a>=b: 令 a=qbq*b+r; 由于d|a d|(qb)(q*b) 那么易得 d|(a-qbq*b) 即得d|r 又r=a mod b ,所以gcd(a,b)=gcd(b,a%mod);

欧拉函数

φ(N)=N*(1 - 1p1\displaystyle\frac{1}{p_1})(1 - 1p2\displaystyle\frac{1}{p_2})(1 - 1p3\displaystyle\frac{1}{p_3})......(1 - 1pm\displaystyle\frac{1}{p_m})

在这里插入图片描述

证明在这里插入图片描述 具体用到了容斥原理: φ(N)=N - (Np1\displaystyle\frac{N}{p_1} + Np2\displaystyle\frac{N}{p_2} + Np3\displaystyle\frac{N}{p_3} + Np4\displaystyle\frac{N}{p_4}- Np1p2\displaystyle\frac{N}{p_1p_2} - Np2p3\displaystyle\frac{N}{p_2p_3}-Np1p3\displaystyle\frac{N}{p_1p_3} - Np1p4\displaystyle\frac{N}{p_1p_4} - Np2p4\displaystyle\frac{N}{p_2p_4} - Np3p4\displaystyle\frac{N}{p_3p_4} + Np1p2p3\displaystyle\frac{N}{p_1p_2p_3} + Np1p2p4\displaystyle\frac{N}{p_1p_2p_4}...-...+..-..Np1p2p3......pm\displaystyle\frac{N}{p_1p_2p_3......p_m}) (ps:分式这小东西麻烦啦啊aaa...) 上面的式子因式分解就得到了:在这里插入图片描述 即是: φ(N)=N*(1 - 1p1\displaystyle\frac{1}{p_1})(1 - 1p2\displaystyle\frac{1}{p_2})(1 - 1p3\displaystyle\frac{1}{p_3})......(1 - 1pm\displaystyle\frac{1}{p_m})

欧拉函数的一些性质

在这里插入图片描述

2.性质2我们知道了欧拉函数是积性函数(PS:积性函数:对于任意互质的整数a和b有性质f(ab)=f(a)f(b)的数论函数)

3.p 为质数,若p|n 且 p2p^2|n,则φ(n)=φ(np)pφ(\displaystyle\frac{n}{p})*p;

4.p 为质数,若p|n 且 p2p^2不n整除,则φ(n)=φ(np)(p1)φ(\displaystyle\frac{n}{p})*(p-1);

5.dnφ(d)=n\sum_{d|n}φ(d)=n

证明

1.由于gcd(n,x)=gcd(n,n-x)=1,所以与n互质的数是"成对"的出现。进而n+(n-x)=n. 就得出了 2.性质2就很显然了,根据欧拉函数的公式 φ(N). a,b互质,所以分解质因数没有一样的,根据欧拉函数就可以将 φ(a) φ(b)合并得出了φ(N)。 所以欧拉函数是积性函数 3.用到了 φ(N)=N * (1 - 1p1\displaystyle\frac{1}{p_1})(1 - 1p2\displaystyle\frac{1}{p_2})(1 -1p3\displaystyle\frac{1}{p_3})......(1 - 1pm\displaystyle\frac{1}{p_m}) p2p^2|n主要告诉咱们N分解后p的质因数个数不少于2. 所以Np\displaystyle\frac{N}{p}分解后的质因数还有p. φ(NN)和φ(Np\displaystyle\frac{N}{p})的欧拉函数表达式中(1 - 1pi\displaystyle\frac{1}{p_i})全都一样,只是一个是N和Np\displaystyle\frac{N}{p}。 所以就得出了φ(N)=φ(Np)pφ(\displaystyle\frac{N}{p})*p; 4.是同理3的证明方法。 5.性质5:f(n)=dnφ(d)=nf(n)=\sum_{d|n}φ(d)=n 证明比较难了,首先得证明f(n)f(n)是积性函数. f(n)f(n)积性函数证明:其实有点显然了, 若n,m互质,出发点仍然是质因数分解考虑. 举个栗子:n=6, m=35, n,m互质. n中的d:1,2,3. m中的d:1,5,7. 然后我们知道欧拉函数 φ(N)是个积性函数(见性质2)。 (1,2,3)(1,5,7)(1,2,3)*(1,5,7)的分解得出集合d全是能整除nmn*m,不多不少。 ps:(1,2,3)(1,5,7)(1,2,3)*(1,5,7) 得到的d集合(1,5,7,2,10,14,3,15,21). 所以就知道了嘛: n,m互质 那么:f(nm)=f(n)f(m)f(n*m)=f(n)f(m) 得证:f(n)f(n)是积性函数

在这里插入图片描述 ps:φ(1)=1

同余

定义:若整数a和整数b除以正整数m的余数相等,则称a,b模m同余。记为a \equiv b(mod m)。 eg:10 \equiv 24(mod 7) 同余数3.

同余的基本性质

在写性质之前,先得有种这样的思路。 a=pmp*m+r1r_1 b=qmq*m+r2r_2
如果a\equivb(mod m),那么r1r_1=r2r_2

性质一: 如果a\equivb(mod m),c\equivd(mod m).那么(a±ca\pm c)\equiv(b±db \pm d) (mod m)。

(ps:从r1r_1r2r_2这方面思考)。

性质二:如果a\equivb(mod m),c\equivd(mod m).那么(aca*c)\equiv(bdb*d) (mod m)。

证明: 首先从已知得到a和b的余数是r1r_1,c和d的余数是r2r_2. a=pmp*m+r1r_1, c=qmq*m+r2r_2
然后呢aca*c=pqmp*q*m +(r1+r2)m(r_1+r_2)*m +r1r2r_1*r_2. 对于bdb*d同理的,那些(pqmp*q*m +(r1+r2)m(r_1+r_2)*m)能被整除的不用管它,然后后面的尾巴都是r1r2r_1*r_2.所以得以证明。

性质三: 如果ac\equivbc(mod m), 且 gcd(c,m)=1(c和m互质) , 则有a\equivb(mod m)。

这条性质很重要,反复品味。 证明: 对于ac\equivbc(mod m)有ac=pmp*m+r1r_1,bc=qmq*m+r1r_1。 所以ac-pmp*m=bc-qmq*m. 得到:(ab)c(a-b)*c=(pq)m(p-q)*m。 再可得到(ab)c(a-b)*c中能被m整除。 然后根据算术基本定理:n=p_{1}^{c_1}$$p_{2}^{c_2}$$p_{3}^{c_3}$$p_{4}^{c_4}......pmcmp_{m}^{c_m} 由于gcd(c,m)=1, m不在c中,那么m只能在aba-b中。(即是aba-b=kmk*m) 用符号表示:m|(a-b). a=pmp*m+r1r_1 b=qmq*m+r2r_2
a-b=(pq)m(p-q)*m+(r1r2)(r_1-r_2) 能被m整除,所以r1r_1=r2r_2 这就说明了a和b的余数都是r1r_1。 所以a\equivb(mod m)。

性质四:若ac≡bd,c≡d(mod m),且gcd(c,m)=1,得到a≡b(mod m)。

(ps:其实性质三是性质四的特殊情况,c=d ) 证明: 首先c与d的余数相等,记为r1r_1, 又因为gcd(c,m)=1; 所以r1r_1=1 所以我们得出了gcd(d,m)=1. a=pmp*m+r2r_2 c=qmq*m+11 b=kmk*m+r3r_3 ac=(pqmp*q*m) + (pmp*m) + (qmr1q*m*r_1) + r2r_2 bd=.........+r3r_3 由于ac与bd余数相等,所以r2=r3r_2=r_3。 所以a≡b(mod m)。

同余类与剩余系

在这里插入图片描述 gcd(aba*b,m)=1; gcd(aba*b,m)=gcd((ab)(a*b)%m,m)=1. (ps:见最大公约数性质二) 所以(ab)(a*b)%m 与 m互质。 所以得出了简化剩余系关于模m乘法封闭。 (ps:这个关系怎么理解呢?对于所有的a,gcd(a,m)=1,那么gcd(a%m,m)=1,即是所有a都能映射到m的简化剩余系中)

费马小定理 & 欧拉定理

1.费马小定理:若p是质数,则对于任意整数a,有apaa^p\equiv a(mod p)。 2.欧拉定理:若正整数a,n互质,则aφ(n)1a^{φ(n)}\equiv 1(mod n). ps:我个人一般会写成aφ(p)1a^{φ(p)}\equiv 1(mod p). 证明: 设n的简化剩余系为{a1\overline{a_1},a2\overline{a_2},a3\overline{a_3},\cdots$$\overline{a_{φ(n)}}}。对于ai,aj\forall a_i,a_j,若aaiaaja*a_i\equiv a*a_j(mod n)。 根据同余性质三的结论(如果ac\equivbc(mod m), 且 gcd(c,m)=1(c和m互质) , 则有a\equivb(mod m)。) 可知道aiaja_i\equiv a_j (mod n)又因为ai,aj(1,n)a_i,a_j\in(1,n),所以aiaja_i\neq a_j。 这个证明有什么用呢?意味着若aiaja_i \neq a_j,则(aai)(a*a_i)%n和(aaj)(a*a_j)%n不等。 那么aaia*a_i等一系列是一一对应的。 因此,集合{a1\overline{a_1},a2\overline{a_2},a3\overline{a_3},\cdots$$\overline{a_{φ(n)}}}与集合{aa1\overline{aa_1},aa2\overline{aa_2},aa3\overline{aa_3},\cdots$$\overline{aa_{φ(n)}}}都能表示n的简化剩余系。 所以我们得出了这样一个东东.... aφ(n)a1a2a3aφ(n)(aa1)(aa2)(aa3)aφ(n)a^{φ(n)}a_1a_2a_3\cdots a_{φ(n)}\equiv (aa_1)(aa_2)(aa_3)\cdots a_{φ(n)} (aa1)(aa2)(aa3)aφ(n)a1a2a3aφ(n)(aa_1)(aa_2)(aa_3)\cdots a_{φ(n)}\equiv a_1a_2a_3\cdots a_{φ(n)}(mod n) aa1=p1maa_1=p_1*m+rir_i,aa2=p2maa_2=p_2*m+rjr_j,aa3=p3maa_3=p_3*m+rkr_k,然后像这样所有的aasaa_s的余数ri,rj,rkr_i,r_j,\cdots r_k组成的集合就是n的简化剩余系(ps:用到了上面证明的两个集合一一对应的性质,我相信您能看到这里对同余的理解应该很清楚了)就得出了上面的等式。 结合两个等式aφ(n)a1a2a3aφ(n)a1a2a3aφ(n)a^{φ(n)}a_1a_2a_3\cdots a_{φ(n)}\equiv a_1a_2a_3\cdots a_{φ(n)}(mod n) a1a2a3aφ(n)a_1a_2a_3\cdots a_{φ(n)}与n互质(根据同余性质三) 得出了aφ(n)1a^{φ(n)} \equiv 1(mod n) 在上述中我们的前提是a与n是互质的。故我们证明了欧拉定理。 如果n是质数,那么φ(n)=n-1。 在欧拉定理的基础上左右乘以a,得到anaa^n\equiv a(mod n) 即证明了费马小定理

趁热打铁来一道关于BSBG的算法题(依据费马下定理)

Discrete Logging POJ - 2417

问题: 给定a,b,p,a,b,p,求最小非负整数x,满足axba^x\equiv b(mod pp) p是质数。 令x=imj,x=i*m -j, m=pm=⌈\sqrt{p}⌉, 所以aimjba^{im-j}\equiv b(mod p) 左右边同乘以aja^j,得到:aimbaja^{im}\equiv ba^j(mod p) 然后就可以枚举i和j了。 首先从[0,m][0,m]枚举j,将得到的baja^j的值存入hash值。 然后,从[1m][1-m]枚举ii,计算aima^{im},查表,如果有值与之相等,则得到的imjim-j是最小值. 然后最小的非负数整数x若存在一定在1-p 区间内。 注意事项

1.为什么最小的非负数整数x若存在一定在[1,p][1,p] 区间内 因为费马小定理:apaa^p\equiv a(mod p) 进而得出ap11a^{p-1}\equiv 1(mod p) x=k*(p-1)+r. 1<r<p-1 所以:axra^x\equiv r(mod pp) 这说明了什么呢,一个大于等于p的x,总能在r [1,p1]\in [1,p-1],找出r。 2.为什么m要取n⌈\sqrt{n}⌉就可以? 因为是枚举嘛,开根号的时间复杂度是多少的。 3.一点j点可能会被覆盖呀??对没错。枚举j时出来的值是有可能重复,那么在map就会再次刷新, 然后若是刷新啦,j刷新值会比原来值更大(ps:在代码中解释会更直观)。j值越大,得到的x越小(x=imjx=i*m -j),也是我们希望的。 4.关于枚举i,j区间的问题: 为什么从0−m枚举j,而从1−m枚举i? i不能为0,否则im−j有可能出现负数的情况. hmmm 其实枚举i,j的区间并不是一定的。 依据:x=imjx=i*m -j 还能枚举有: i[1,m]i\in [1,m] , j[1,m]j\in [1,m] 参考资料:BSGS算法 学习笔记

#include<iostream>
#include<cstdio>
#include<cmath>
#include<map>
#define ll long long
#define rep(i,a,b) for(int i=a;i<=b;i++)
ll gcd(ll a,ll b){ return b? gcd(b,a%b):a;}
//const int N=2e5+10;
const ll mod=1e9+7;
using namespace std;
ll read(){
    ll s = 0, f = 1; char ch = getchar();
    while(!isdigit(ch)){
        if(ch == '-') f = -1;
        ch = getchar();
    }
    while(isdigit(ch)) s = (s << 3) + (s << 1) + (ch ^ 48), ch = getchar();
    return s * f;
}
ll a,b,p;
ll qpow(ll x,ll y){
	ll ans=1;
	while(y){
		if(y&1){
			ans=(ans*x)%p;
		}
			x=(x*x)%p;
			y>>=1;
	}
	return ans;
}
ll x;
map<ll,int> mp;
void solve(){
//	cout<<qpow(2,10)<<endl;
    while(scanf("%lld %lld %lld",&p,&a,&b)!=EOF){
         a%=p;
         if(a==0 and b==0) {
             printf("%d\n",1);
             return ;
         }
         if(a==0)
         {  
         printf("no solution\n");
             return ;
         }
    	 //注意一个小细节,就是在快速幂中是p哦,别写成了mod啦,一个小小的坑~
    	  ll m=ceil(sqrt(p));
    	  mp.clear();
    	  ll ans=b%p;
    	  //枚举区间i:1-m 
    	  for(int i=1;i<=m;i++){
				ans=ans*a%p;
				mp[ans]=i;
//				cout<<ans<<" "<<(i+1)<<endl;
		  }
		  ans=1;
		  int flag=0;
		  ll  t=qpow(a,m);
//		  ll top=p/m+1;
		  for(int i=1;i<=m;i++){
			  ans=ans*t%p;
		  	if(mp[ans]){
//		  		cout<<(i*m-mp[ans]+p)%p<<endl;
                printf("%lld\n",(i*m-mp[ans]+p)%p);
		  		flag=1;
		  		break;
			  }
		  }
		  if(!flag) printf("no solution\n");
//		   cout<<"no solution"<<endl;
	}
}
int main (){
//  freopen("in.txt","r",stdin);
//  freopen("out.txt","w",stdout);
  solve();
  return 0;
}

欧拉定理的推论

最小正整数x0x_0

在这里插入图片描述

欧拉的指数循环节

在这里插入图片描述 在这里插入图片描述

其他理论

1.在这里插入图片描述 i在区间[x,g(x)]内,k/i\lfloor k/i \rfloor 的值都相等。

说明

求出i的一段连续区间使得:k/i\lfloor k/i \rfloor 是个定值。 eg: k=33,i=10; 33/10\lfloor 33/10 \rfloor=3; 但是呢? 33/8\lfloor 33/8 \rfloor=3; 满足 33/9\lfloor 33/9 \rfloor=3; 满足 33/11\lfloor 33/11 \rfloor=3; 满足 33/12\lfloor 33/12 \rfloor=2; 不满足 所以他的一段连续的区间是:[8,11]。 i在[8,11]内, k/i\lfloor k/i \rfloor是个定值。

证明在这里插入图片描述 由于g(x)>=x,所以i在这个区间内是个定值。

来看一到题:余数之和

题意很简单: 在这里插入图片描述

这里就用上了该结论。 大概讲一下写代码的思路。 我们知道一个N的正因数个数不超过2* sqrt(N).
(ps:a<b aba*b=N 那么a<sqrt(n) 和 b>sqrt(n) 所以显而易见) 知道了这个就可以说明了 i在[1,N]内 N/i\lfloor N/i \rfloor 值不同的个数不超过 2N2* \sqrt{N}. 然后就知道了有不超过2* sqrt(N)个的区间。时间复杂度在O(N\sqrt{N}); AC代码:

#include<bits/stdc++.h>
#define ll long long
#define rep(i,a,b) for(int i=a;i<=b;i++)
ll gcd(ll a,ll b){ return b? gcd(b,a%b):a;}
const int N=2e5+10;
const ll mod=1e9+7;
ll read(){
    ll s = 0, f = 1; char ch = getchar();
    while(!isdigit(ch)){
        if(ch == '-') f = -1;
        ch = getchar();
    }
    while(isdigit(ch)) s = (s << 3) + (s << 1) + (ch ^ 48), ch = getchar();
    return s * f;
}
using namespace std;
ll n,k;
ll sum=0;
void solve(){
    n=read();k=read();
	sum=n*k;
	ll x=1;
	ll y;
	ll cnt;
	while(x<=n){
		if(k/x !=0)
		 y=k/(k/x);
		 else y=n;
		 cnt=y-x+1;
		if(y<=n)
		sum-= (k/x)*cnt*(x+y)/2;
		else {
			sum-=(k/x)*(n-x+1)*(x+n)/2;
		}
		x=y+1;
	}
	cout<< sum<<endl;
}
int main (){
//   freopen("in.txt","r",stdin);
//   freopen("out.txt","w",stdout);
  solve();
//   while(1);
  return 0;
}

参考博客: 同余 Discrete Logging BSGS算法