数论是纯粹数学的分支之一,主要研究整数的性质。整数可以是方程式的解(丢番图方程)。有些解析函数(像黎曼ζ函数)中包括了一些整数、质数的性质,透过这些函数也可以了解一些数论的问题。透过数论也可以建立实数和有理数之间的关系,并且用有理数来逼近实数(丢番图逼近)。按研究方法来看,数论大致可分为初等数论和高等数论。初等数论是用初等方法研究的数论,它的研究方法本质上说,就是利用整数环的整除性质,主要包括整除理论、同余理论、连分数理论。高等数论则包括了更为深刻的数学研究工具。它大致包括代数数论、解析数论、计算数论等等。//引自百度百科
建议搭配食用:我的小目录
1.1 整除
设a是非零整数,b是整数。如果存在一个整数q,使得b=a*q,那么就说b可被a整除,记作a∣b,且称b是a的倍数,a是b的约数(因子)
整除具有以下一些性质
- 如果a∣b且b∣c,那么a∣c
- a∣b且a∣c等价于对任意的整数x和y,有a∣(b×x+c×y)
- 设m≠0,那么a∣b等价于(m×a)∣(m×b)
- 设整数x和y满足下式:a×x+b×y=1,且a∣n,b∣n,那么(a×b)∣n
证明略
- 若b=q×d+c,那么d∣b的充要条件是d∣c
(因为一些傻狗原因,这个*不太好用)
没有例题
1.2 同余
若a,b为两个整数,且他们的差a-b能被某个自然数m所整除,则称a在%m意义下与b同余,记为a≡b(mod m)。它意味着,a-b=m*k(k为一个整数)
对于整数a,b,c和自然数m,n,对模m同余具有以下性质
- 自反性 a≡a(mod m)
- 对称性 若a≡b(mod m),则b≡a(mod m)
- 传递性 若a≡b(mod m),b≡c(mod m),则a≡c(mod m)
- 同加性 若a≡b(mod m),则a+c≡b+c(mod m)
- 同乘性 若a≡b(mod m),则a×c≡b×c(mod m);若a≡b(mod m),c≡d(mod m),则a×c≡b×d(mod m)
- 同幂性 若a≡b(mod m),则aⁿ≡bⁿ(mod m)
- 推论1: a*b mod k = (a mod k) * (b mod k) mod k
- 推论2: 若a mod p = x,a mod q = x,p、q互质,则a mod p*q = x
-
同余不满足同除性
例题:洛谷P1226
快速幂取模
输入b,p,k的值,求b^p mod k的值。其中b,p,k*k为长整型数。
输入输出格式
输入格式:
三个整数b,p,k.
输出格式:
输出“b^p mod k=s”
s为运算结果
输入输出样例
输入样例#1:
2 10 9
输出样例#1:
2^10 mod 9=7
#include <bits/stdc++.h>
using namespace std;
typedef long long ll
ll b,p,k;
ll ans;
ll pow_mod(ll t) {
if (t == 1) return b%k;
ans = Mod(t/2);
ans = (ans*ans)%k;
if (t%2 == 1) ans = (ans*(b%k))%k;
return ans;
}
int main() {
cin >> b >> p >> k;
cout << b << '^' << p << " mod " << k << '= '<< pow_mod(p) << endl;
return 0;
}
这个看一眼推论一就会了⑧
1.3 最大公约数(GCD)
最大公因数,也称最大公约数、最大公因子,指两个或多个整数共有约数中最大的一个。
求最大公约数有多种方法,常见的有质因数分解法、短除法、辗转相除法、更相减损法。与
最大公约数相对应的概念是最小公倍数。
注:a,b的最大公约数记为(a,b),同样的,a,b,c的最大公约数记为(a,b,c),a,b的最小公倍数记为[a,b]。(这个写法只有天朝能用哦,老外不认的哦)
1 辗转相除法
原理:GCD(x,y)=GCD(y,y-x)
int GCD(int x,int y) {
return y == 0 ? x : GCD(y,x%y);
}
2 二进制算法
通过不断去除因子2来降低常数
若x=y,则GCD(x,y)=x,否则
(1) 若x,y均为偶数,则GCD(x,y)=2*GCD(x/2,y/2)
(2) 若x为偶数,y为奇数,则GCD(x,y) = GCD(x/2,y)
(3) 若x为奇数,y为偶数,则GCD(x,y) = GCD(x,y/2)
(4) 若x,y均为奇数,则GCD(x,y) = GCD(x-y,y)
inline int GCD(int x,int y) {
int i,j;
if (x == 0) return y;
if (y == 0) return x;
for (i = 0;0 == (x & 1);++i) x >>= 1;
for (j = 0;0 == (y & 1);++j) y >>= 1;
//去掉所有的2
if (j<i) i = j;
while (1) {
if (x < y ) swap(x,y);
if (0 == (x -= y)) return y << i;
//辗转相减
while (0 == (x&1)) x >>= 1;
//去掉所有的2
}
}
3.最小公倍数(LCM)
求解方法:
∀整数a,b,n
1. 逐步倍增
让n从1开始逐步加1并判断b∣a*n是否成立,成立时a*n即为a,b的最小公倍数
2. 定理:GCD(a,b)×LCM(a,b)=a×b
4 扩展欧几里得算法
已知a, b,求解一组x,y,使它们满足a×x+b×y =gcd(a, b)
证明:
假设 a>b,
(1) b=0 gcd(a,b) = a , ax = a , 则x=1,y=0;(这里我还是推荐不把gcd(a,0)理解成最大公约数,而是一个计算机求出来的值)
(2) 假设 ax1+by1=gcd(a,b) (方程一) bx2+(a%b)y2=gcd(b,a%b)(方程二);由欧几里得算法gcd(a,b) =gcd(b,a%b) 得到,
ax1+by1 = bx2+(a%b)y2,即ax1+by1=bx2+(a-a/bb)y2 ax1+by1=ay2+b(x2-a/by2)
在根据多项式恒等定理(把a,b看成变量),x1=y2; y1=x2-a/b*y2;
(表面上看,就是已知方程一的一组解,可以得到方程二的一组解,已知方程二的一组解,就可以得到方程一的一组解,但是实际情况是,不可能先知道方程一的解(x1,y1)。)上述思想是递归定义的,不断地利用gcd(a,b) =gcd(b,a%b),到b=0(y的系数为0)时,由(1)的解,根据解之间的关系,最终可以得到方程ax+by =gcd(a, b)的解。
代码如下
#include<bits/stdc++.h>
using namespace std;
int exgcd(int a,int b,int &x,int &y) {
if (b == 0) {
x = 1,y = 0;
return a;
}
int gcd = exgcd(b,a%b,x,y);
int x2 = x,y2 = y;
x = y2;
y = x2-(a/b)*y2;
return gcd;
}
int main() {
int x,y,a,b;
cin>>a>>b;
cout << exgcd(a,b,x,y) << endl;
cout << x << " " << y << endl;
return 0;
}
5 求解线性同余方程
定理1 : 对于方程a * x+b * y=c,该方程等价于a*x≡c(mod b),有整数解的充分必要条件是:c%GCD(a,b)=0
应用:对于方程a * x+b * y = c,我们可以先用扩展欧几里得算法求出一组解x0,y0,然后两边同时除以GCD(a,b),再乘以c。这样就得到了方程a * x0 * c / GCD(a,b) + b * y0 * c / GCD(a,b) = c,我们也就找到了方程的一组解。
定理2 : 若GCD(a,b) = 1,且x0,y0为a * x+b * y = c的一组解,则该方程的任一解可表示为x = x0+b * t,y = y0-a * t,且对于任一整数t,皆成立
应用2 : 根据定理2,可以求出方程的所有解。但在实际问题中,我们往往被要求去求最小整数解,也就是去求一个特解x,t=b/GCD(a,b),x=(x%t+t)%t,代码如下:
int Ex_Eu(int a,int b,int &x,int &y) {
if (b == 0) {
x = 1,y = 0;
return a;
}
int d = Ex_Eu(b,a%b,x,y);
int temp = x,x =y,y = temp-a/b*y;
return d;
}
//用扩展欧几里得算法解线性方程ax+by=c
bool linearEquation(int a,int b,int c,int &x,int &y) {
int d = Ex_Eu(a,b,x,y);
if (c%d) return false;
int k = c/d;
x *= k;
y *= k;
return true;
}
例题time
LuoguP1290欧几里得的游戏
欧几里德的两个后代Stan和Ollie正在玩一种数字游戏,这个游戏是他们的祖先欧几里德发明的。给定两个正整数M和N,从Stan开始,从其中较大的一个数,减去较小的数的正整数倍,当然,得到的数不能小于0。然后是Ollie,对刚才得到的数,和M,N中较小的那个数,再进行同样的操作……直到一个人得到了0,他就取得了胜利。下面是他们用(25,7)两个数游戏的过程:
Start:25 7
Stan:11 7
Ollie:4 7
Stan:4 3
Ollie:1 3
Stan:1 0
Stan赢得了游戏的胜利。
现在,假设他们完美地操作,谁会取得胜利呢?
输入输出格式
输入格式:
第一行为测试数据的组数C。下面有C行,每行为一组数据,包含两个正整数M, N。(M, N不超过长整型。)
输出格式:
对每组输入数据输出一行,如果Stan胜利,则输出“Stan wins”;否则输出“Ollie wins”
输入输出样例
输入样例#1:
2
25 7
24 15
输出样例#1:
Stan wins
Ollie wins
参考程序
#include <bits/stdc++.h>
using namespace std;
int main() {
int c;
cin >> c;
for (int i = 0;i < c;i++) {
int m,n;
cin >> m >> n;
if (m < n) {
swap(m,n);
}
int f = 1;
while (m/n == 1 && m%n) {
int t = m%n;
m = n;
n = t;
f = -f;
}
if (f == 1) {
cout << "Stan ins\n";
} else cout << "Ollie ins\n";
}
return 0;
}
1.4 逆元
方程ax≡1(mod p),的解称为a关于模p的逆,当gcd(a,p)==1(即a,p互质)时,方程有唯一解,否则无解。
对于一些题目会要求把结果MOD一个数,通常是一个较大的质数,对于加减乘法通过同余定理可以直接拆开计算,
但对于(a/b)%MOD这个式子,是不可以写成(a%MOD/b%MOD)%MOD的,但是可以写为1/(a*b)其中b^-1表示b的逆元。
利用快速幂及扩展欧几里得算法求逆元的代码如下:
void exgcd(int a,int b,int c,int &x,int &y) {
if (a = 0) {
x = 0,y = c/b;
return ;
} else {
int tx,ty;
exgcd(b%a,a,tx,ty);
x = ty-(b/a)*tx;
y = tx;
return ;
}
}
线形算法:
过程略,其实是我忘了是啥了
A[i] = -(p*i)*A[p%i];
递归即可,时间复杂度Θ(log₂p)
LuoguP3811乘法逆元模板
题目背景
这是一道模板题
题目描述
给定n,p求1~n中所有整数在模p意义下的乘法逆元。
输入格式
一行n,p
输出格式
n行,第i行表示i在模p意义下的逆元。
代码如下:
#include<cstdio>
using namespace std;
long long p,c[3000005];
int main()
{
long long n;
scanf("%lld%lld",&n,&p);
c[1]=1;
printf("1\n");
for(register int i=2; i<=n; i++)
{
c[i]=(p-p/i)*c[p%i]%p;
printf("%lld\n",c[i]);
}
return 0;
}
//线形递推算法
1.5 孙子 中国剩余定理
注一下:(此部分中部分材料引自:巨佬的博客)
问题:今有物不知其数,三三数之剩二,五五数之剩三,七七数之剩二。问物几何?
强行翻译:∀x,模3等于2,模5等于三,模7等于二,求x。
//辅助材料姬
定理1:两个数相加,如果存在一个加数,不能被整数a整除,那么它们的和,就不能被整数a整除。
定理2:两数不能整除,若除数扩大(或缩小)了几倍,而被除数不变,则其商和余数也同时扩大(或缩小)相同的倍数(余数必小于除数)。
具体步骤:
1、求出最小公倍数
lcm=357=105
2、求各个数所对应的基础数
(1)105÷3=35
35÷3=11…2 //基础数35
(2)105÷5=21
21÷5=4…1
定理2把1扩大3倍得到3,那么被除数也扩大3倍,得到213=63//基础数63
3、105÷7=15
15÷7=2…1
定理2把1扩大2倍得到2,那么被除数也扩大2倍,得到152=30//基础数30
把得到的基础数加和 (注意:基础数不一定就是正数)
35+63+30=128
4、减去最小公倍数lcm(在比最小公倍数大的情况下)
x=128-105=23
总结:已知m1,m2,m3是两两互质的正整数,求最小的正整数x,使它被m1,m2,m3除所得的余数分别是c1,c2,c3。孙子 (不是很雅观啊)中国剩余定理的思想便是先分别求出被其中数mi整除余1而被另外两个数整除的数Mi(i=1,2,3),则所求数之一的便是c1M1+c2M2+c3M3。由此我们可以得到n个两两互质数的情况。
例题time:
POJ1006:Biorhythms
生理周期
Time Limit: 1000MS Memory Limit: 10000K
Total Submissions: 150107 Accepted: 48558
Description
人生来就有三个生理周期,分别为体力、感情和智力周期,它们的周期长度为23天、28天和33天。每一个周期中有一天是高峰。在高峰这天,人会在相应的方面表现出色。例如,智力周期的高峰,人会思维敏捷,精力容易高度集中。因为三个周期的周长不同,所以通常三个周期的高峰不会落在同一天。对于每个人,我们想知道何时三个高峰落在同一天。对于每个周期,我们会给出从当前年份的第一天开始,到出现高峰的天数(不一定是第一次高峰出现的时间)。你的任务是给定一个从当年第一天开始数的天数,输出从给定时间开始(不包括给定时间)下一次三个高峰落在同一天的时间(距给定时间的天数)。例如:给定时间为10,下次出现三个高峰同天的时间是12,则输出2(注意这里不是3)。
Input
输入四个整数:p, e, i和d。 p, e, i分别表示体力、情感和智力高峰出现的时间(时间从当年的第一天开始计算)。d 是给定的时间,可能小于p, e, 或 i。 所有给定时间是非负的并且小于365, 所求的时间小于21252。
当p = e = i = d = -1时,输入数据结束。
Output
从给定时间起,下一次三个高峰同天的时间(距离给定时间的天数)。
采用以下格式:
Case 1: the next triple peak occurs in 1234 days.
注意:即使结果是1天,也使用复数形式“days”。
Sample Input
0 0 0 0
0 0 0 100
5 20 34 325
4 5 6 7
283 102 23 320
203 301 203 40
-1 -1 -1 -1
Sample Output
Case 1: the next triple peak occurs in 21252 days.
Case 2: the next triple peak occurs in 21152 days.
Case 3: the next triple peak occurs in 19575 days.
Case 4: the next triple peak occurs in 16994 days.
Case 5: the next triple peak occurs in 8910 days.
Case 6: the next triple peak occurs in 10789 days.
解法:
先来看一个故事
“韩信点兵”:传说西汉大将韩信,由于比较年轻,开始他的部下对他不很佩服。有一次阅兵时,韩信要求士兵分三路纵队,结果末尾多2人,改成五路纵队,结果末尾多3人,再改成七路纵队,结果又余下2人,后来下级军官向他报告共有士兵2395人,韩信立即笑笑说不对(因2395除以3余数是1,不是2),由于已经知道士兵总人数在2300~2400之间,所以韩信根据23,128,233,------,每相邻两数的间隔是105(3、5、7的最小公倍数),便立即说出实际人数应是2333人(因2333=128+20χ105+105,它除以3余2,除以5余3,除以7余2)。这样使下级军官十分敬佩,这就是韩信点兵的故事。
韩信点兵问题简化:已知 n%3=2, n%5=3, n%7=2, 求n。 再看我们这道题,读入p,e,i,d 4个整数
已知(n+d)%23=p; (n+d)%28=e; (n+d)%33=i ,求n 。
两道题是一样的。但是韩信当时是怎样计算出结果的? 他用的就是“中国剩余定理” “韩”问题计算如下: 因为n%3=2, n%5=3,
n%7=2 且 3,5,7互质 (互质可以直接得到这三个数的最小公倍数) 令x= n%3=2 , y= n%5=3 ,z= n%7=2
使5×7×a被3除余1,有35×2=70,即a=2;
使3×7×b被5除余1,用21×1=21,即b=1;
使3×5×c被7除余1,用15×1=15,即c=1。 那么n =(70×x+21×y+15×z)%lcm(3,5,7) = 23 这是n的最小解 而韩信已知士兵人数在2300~2400之间,所以只需要n+i×lcm(3,5,7)就得到了2333,此时i=22
同样,这道题的解法就是: 已知(n+d)%23=p; (n+d)%28=e; (n+d)%33=i
使33×28×a被23除余1,用33×28×8=5544;
使23×33×b被28除余1,用23×33×19=14421;
使23×28×c被33除余1,用23×28×2=1288。
因此有(5544×p+14421×e+1288×i)% lcm(23,28,33) =n+d 又23、28、33互质,即lcm(23,28,33)= 21252;
所以有n=(5544×p+14421×e+1288×i-d)%21252 本题所求的是最小整数解,避免n为负,因此最后结果为n= [n+21252]% 21252 那么最终求解n的表达式就是:
n=(5544p+14421e+1288*i-d+21252)%21252;
代码如下:
#include<iostream>
using namespace std;
int main(void)
{
int p,e,i,d;
int time=1;
while(cin>>p>>e>>i>>d)
{
if(p==-1 && e==-1 && i==-1 && d==-1)
break;
int lcm=21252; // lcm(23,28,33)
int n=(5544*p+14421*e+1288*i-d+21252)%21252;
if(n==0)
n=21252;
cout<<"Case "<<time++<<": the next triple peak occurs in "<<n<<" days."<<endl;
}
return 0;
}
还有这个,中国剩余定理模板
/*long long gcd(LL a,LL b)
{
return b==0?a:gcd(b,a%b);
}*/
#include<cstdio>
#define ll long long
//扩展欧几里得算法
void gcd(ll a,ll b,ll &d,ll &x,ll &y)
{
if(b==0){
d=a;
x=1,y=0;
}
else{//else不能省略
gcd(b,a%b,d,y,x);
y-=(a/b)*x;
}
}
//中国剩余定理
ll China(int n,ll *m,ll *a)
{
ll M=1,d,y,x=0;
for(int i=0;i<n;i++) M*=m[i];
for(int i=0;i<n;i++){
ll w=M/m[i];
gcd(m[i],w,d,d,y);
x=(x+y*w*a[i])%M;
}
return (x+M)%M;
}
ll m[15],a[15];
int main()
{
int n;
scanf("%d",&n);
for(int i=0;i<n;i++)
scanf("%lld%lld",&m[i],&a[i]);
printf("%lld",China(n,m,a));
}
1.6 卡特兰数
卡特兰数是一个很常用的东西,嗯,具体介绍百度吧
她的前几项为:1、2、5、14、42、132、429、1430、4862、16796、58786、208012、742900、…
求卡特兰数的第n项,可以用以下几个公式:
1.递归1
n-1
f(n) = ∑f(i)f(n-i-1)
i
2.递归2
f(n) = (f(n-1) * (4n-2))/n+1
3.组合1
f(n)=C(n,2n)/n+1
4.组合2
f(n)=C(n,2n)-C(n-1,2n)
1.7 素数
素数又称质数,是指一个大于一的正整数,且除了1和它本身之外没有任何因子,反之就是合数,1既不是素数也不是合数。
1 素数的判定
穷举法略
筛法
void prime() {
memset(p,1,sizeof(p));
p[0] = false,p[1]=false;
int N = 31700;
for (int i = 2;i < N;i++) {
if (p[i) {
p[++cnt] = i;
for (int k = i*i;k < N;k += i) {
p[k] = false;
}
}
}
return ;
}
线性筛
#include <bits/stdc++.h>
using namespace std;
const long N = 200000;
long prime[N] = {0},num_prime = 0;
int isNotPrime[N] = {1,1};
int main() {
for (long i = 2;i < N;i++) {
if (!isNotPrime[i]) prime[num_prime++] = i;
for (long j = 0;j < num_prime && i*prime[j] < N;j++) {
isNotPrime[i*prime[j]] = 1;
if (!(i%prime[j])) break;
}
}
return 0;
}
2 素数的相关定理
2.1 唯一分解定理
若整数a≥2,那么整数a一定可以表示为若干个素数的乘积
·存在性证明:
假设存在大于1的自然数不能写成质数的乘积,把最小的那个称为n。
自然数可以根据其可除性(是否能表示成两个不是自身的自然数的乘积)分成3类:质数、合数和1。首先,按照定义,n 大于1。其次,n
不是质数,因为质\数p可以写成质数乘积:p=p,这与假设不相符合。因此n只能是合数,但每个合数都可以分解成两个严格小于自身而大于1的自然数的积。设,其中a
和b 都是介于1和n 之间的自然数,因此,按照n 的定义,a 和b 都都可以写成质数的乘积。从而
也可以写成质数的乘积。由此产生矛盾。因此大于1的自然数必可写成质数的乘积。
·唯一性证明:
命题描述:若质数p|ab,则不p|a就p|b
设c=ab;p为质数,若p量尽a,得证;若p量不尽a(即p不是a的因子),证明p量尽b即得证。根据(7-29),因为p为质数,且p量不尽a,所以p、a互质。
用反证法证明如下:设p、a不互质,将有公因子m,因为m是a的因子,p不是a的因子,所以m不等于p,而m是p的因子,这是不可能的。
根据(7-定义15),c/p=t,得到c=pt,又c=ab,根据(7-19)得到p/a=b/t
根据(7-21),因为p、a互质,p、a是具有相同比的数对中最小的一对。
再根据(7-20),大小两数a、p分别量尽具有同比的大小两数t、b,所得的次数相同,即前项量尽前项、后项量尽后项,所以p量尽b,即得证。
最后一条定理(7-20)证明的难度较大,特证明如下:
命题:设a、b是与a/b有相同比的数对中最小的一对数,如果a/b=c/d,则a=(1/n)*c;b=(1/n)*d。
证明:因为a<c,所以a=(1/n)*c或者a=(m/n)*c,[1<m<n]
根据(7-13和定义20),若a=(m/n)*c,那么也有b=(m/n)*d
这样a、b均有(1/m)的部分
根据(7-12),(1/m)*a : (1/m)*b = a : b
但是(1/m)*a 、 (1/m)*b 分别小于 a 、b,这与假设矛盾,所以仅有a=(1/n)*c
根据(7-13和定义20),有a=(1/n)*c,b=(1/n)*d,故得证。
2.2 威尔逊定理
·描述:
p 为质数–>(p-1)!\equiv -1(\mod p)
2.3 费马定理
3 Miller-Rabin 素性测试
4 欧拉定理
5 Pollard Rho破腊肉 算法求大数因子