省选两天出了两道数论,连暴力都不会打。回头一看确实自己的数论什么都不会。从头学起,开博客记录一下。立一个flag:半个月学完数论
基础数论
1.欧几里得和扩展欧几里得
先说几个简单的概念。
-
整除:定义如下:设a是非零整数,b是整数,若存在一个整数q,使得 则称b可被a整除,记作 ,称b是a的倍数,a是b的因数。下面有几个常用性质。
- 传递性:若 ,则 .
- ,有 .
- 设 ,则 .
- 设整数x和y满足
且
,那么
.
- 证明:因为 ,所以
- (性质三)
- 所以 (性质二)
- 其中 .得证。
- 若 ,那么 的充要条件是
-
同余:若a,b为两个整数,且他们的差 能被某个自然数m整除,称a在模m意义下与b同余,记作 ,它意味着 。下面有几个常用性质:
- 自反性,对称性,传递性,同加(乘/幂)性
- .
- 若
- 证明:因为 .
- 则一定 ,使得
- 所以
- 因为gcd(p,q)=1,故t中必有p这个质因子
- 所以
- 所以 .
- 若
- 最大公约数和欧几里得算法
- 最大公约数:设 ,则 ,该定义可以推广到多个数。
- 欧几里得算法(辗转相除法):
- 证明:若 。
- 代码:
int gcd(int x,int y)
{
return !y?x:gcd(y,x%y);
}
进入正题,扩展欧几里得算法
扩展欧几里得算法是用来在已知a,b时,求解一组p,q,使得
如何求解呢?回想起刚才的gcd,我们列出如下等式。
int ex_gcd(int a,int b,int &x,int &y)
{
if (!b)
{
x=1,y=0;
return a;
}
int ret=ex_gcd(b,a%b,x,y);
int tmp=x;x=y;y=tmp-a/b*y;
return ret;
}
int main()
{
int a,b,x,y,z;
scanf("%d%d",&a,&b);
z=ex_gcd(a,b,x,y);
printf("%d %d %d\n",x,y); //x*a+y*b=gcd(a,b)
return 0;
}
求解线性同余方程
定理:对于方程
,有整数解的充要条件是
对于线性同余方程的一般形式
,我们只需要先求出一组方程
,然后我们将等式的两边同时乘以一个
,就得出了原方程的一组解。
定理2:对于上述方程,若已知一组解 ,方程所有解为:
应用定理2,我们就可以得出所有的解,在实际应用中,往往会求最小整数解,也就是最小化x,此时 .
逆元
若 互质,则称x为a在模p意义下的逆元,记作 .
求逆元的几个方法:
- 扩展欧几里得求解:
,将上面的式子带入扩展欧几里得中,求出的x就是逆元
- 递推:
。。。。证明略
- 费马小定理/欧拉定理。
中国剩余定理
设自然数
两两互质,记
,则同余方程组
在模M同余意义下有唯一解
求解方法:
令
在模M意义下,方程组的解为
卡特兰数
卡特兰数是组合学中计数问题经常出现的数列,其前几项为1,2,5,14,42,132…
卡特兰数第n项求法:
1. 递归公式(1):
2. 递归公式(2):
3. 组合公式(1):
4. 组合公式(2):
常用模型:二叉树计数,括号序列,出栈序列
素数相关
素数定义:一个大于1的正整数,如果除了1和它本身之外,没有任何因数,则称其为质数或素数。1既不是质数也不是合数。素数有无穷多个。
素数的相关定理
- 唯一分解定理
- 若整数
,那么a一定可以表示为若干个质数的乘积且唯一,形如
- 若整数
,那么a一定可以表示为若干个质数的乘积且唯一,形如
- 威尔逊定理
- 若p为素数,则
- 若p为素数,则
- 费马小定理
- 若p为素数,a为正整数,且
,则
- 证明:易知 没有一个数是p的倍数,其次上述p-1个数中没有任何两个在模p意义下同余。
- 故p-1个数对在模p意义下的同余是1…p-1的某一种排列,即
- 若p为素数,a为正整数,且
,则
素数的相关知识
- 欧拉函数
表示小于等于n的数中与n互质的数的个数。于是有如下定理。
- 欧拉函数是一个积性函数。
- 若n是素数,则
- 若n是某个素数的整次幂即 ,则
- 若n是两个互质的数a,b的乘积即 ,则
- 设
,则
-
欧拉定理
- 欧拉函数是一个积性函数。
- 素数的判定
- 单个数
- 穷举法:从 枚举并试除。复杂度差劲
- Miller-Rabin算法:一种基于随机化的检验质数的方法,选取合适的检验用数(2,7,61,24251)可以让失误率极极极极极极低。
- 单个数
const int prime[5]={0,2,7,61,24251};
ll quick_mul(ll a,ll b,ll Mod)
{
ll ans=0;a%=Mod,b%=Mod;
while (b)
{
if (b&1) ans+=a,ans%=Mod;
b/=2,a+=a,a%=Mod;
}
return ans;
}
ll quick_pow(ll a,ll b,ll Mod)
{
ll ans=1;a%=Mod,b%=Mod;
while (b)
{
if (b&1) ans*=a,ans%=Mod;
b/=2,a*=a,a%=Mod;
}
return ans;
}
bool check(ll r,ll m,ll x,ll a)
{
ll res=quick_pow(a,m,x);
if (res==x-1 || res==1) return 1;
while (r--)
{
res=quick_mul(res,res,x);
if (res==x-1) return 1;
}
return 0;
}
bool Miller_Rabin(ll x)
{
if (x==2) return 1;
if (x==1 || x%2==0) return 0;
ll m=x-1,r=0;
while (!(m&1))
m/=2,r++;
for (int i=1;i<=4;i++)
{
int a=prime[i];
if (x==a) return 1;
if (x%a==0) return 0;
if (!check(r,m,x,a)) return 0;
}
return 1;
}
- 筛法
- 埃氏筛:将所有质数不大于n的倍数全部筛去,有冗余计算。
- 线性筛:在适当的地方break,保证每个数只被筛去一次。
void get_prime(int n)
visit[1]=1;
for (int i=2;i<=n;i++)
{
if (!visit[i])
a[++cnt]=i;
for (int j=1;j<=cnt && i*a[j]<=n;j++)
{
visit[i*a[j]]=1;
if (i%a[j]==0)
break;
}
}
}
- Pollard Rho求大数质因子
- 原理:基于随机的算法。
- 我们知道,在[1,n]中随机一个数,使这个数是n的因数的概率是很小的。但是如果随机两个数并做差,那么概率将会大大提高。
- 那设随机的两个数为 ,使得 的概率就会大。
- 所以就有了如下算法:
- 随机一个 , 则通过公式 算出,其中c为任意给定值。
- 若满足 ,则证明 是n的一个因数,若其是质数,则记录,否则递归的继续计算。
- 判断素数使用Miller-Rabin算法。
const int prime[5]={0,2,7,61,24251};
vector <int> f;
ll quick_mul(ll a,ll b,ll Mod)
{
ll ans=0;
while (b) {if (b&1) ans+=a,ans%=Mod;a+=a;b/=2;a%=Mod;}
return ans;
}
ll quick_pow(ll a,ll b,ll Mod)
{
ll ans=1;
while (b) {if (b&1) ans*=a,ans%=Mod;a*=a,b/=2,a%=Mod;}
return ans;
}
ll gcd(ll a,ll b)
{
return !b?a:gcd(b,a%b);
}
bool check(ll r,ll m,ll x,ll pri)
{
ll res=quick_pow(pri,m,x);
if (res==x-1 || res==1) return 1;
while (r--)
{
res=quick_mul(res,res,x);
if (res==x-1) return 1;
}
return 0;
}
bool Miller_Rabin(ll x)
{
if (x==2) return 1;
if (x==1 || !(x&1)) return 0;
int m=x-1,r=0;
while (!(m&1))
m/=2,r++;
for (int i=1;i<=4;i++)
{
int pri=prime[i];
if (x==pri) return 1;
if (!(x%pri)) return 0;
if (!check(r,m,x,pri)) return 0;
}
return 1;
}
ll Pollard_Rho(ll n,ll c)
{
ll _x1=rand()%n+1,_x2=_x1;
ll i=1,k=2;
while (1)
{
i++;
_x1=(_x1*_x1+c)%n;
ll t=abs(_x1-_x2);
ll g=gcd(t,n);
if (_x1==_x2) return n;
if (g>1 && g!=n) return g;
if (i==k)
_x2=_x1,k*=2;
}
}
void get(ll n)
{
if (Miller_Rabin(n))
{
f.push_back(n);
return ;
}
ll p=n;
while (p>=n) p=Pollard_Rho(p,rand()%(n-1)+1);
get(p);
get(n/p);
}