数论基础
一.辗转相除求最大公约数
a/b=p…q(q=a%b) -> a=b*p+q -> gcd(a,b)=gcd(b,q)
可持续递归下去直到后面的数为0
时间复杂度:**O(log max(a,b)),**底数为2.
若b>a:gcd(a,b)=gcd(b,a%b)=gcd(b,a);(一次变化为下述情况)。
若a>b:
(1)b>a/2,gcd(a,b)=gcd(b,a%b),a%b=a-b<a/2;
(2)b<a/2,gcd(a,b)=gcd(b,a%b),a%b<b<a/2;
综上,a每次都缩小1/2以上,故时间复杂度为O(log max(a,b))。
补充:lcm(a,b)(最小公倍数)=a*b/gcd(a,b)
int gcd(int a,int b)
{
return b==0?a:gcd(b,a%b);
}
二.扩展欧几里得算法
1.求解x,y满足ax+by=c
裴蜀定理(或贝祖定理):若a,b是整数,且gcd(a,b)=d,那么对于任意的整数x,y,ax+by都一定是d的倍数,特别地,一定存在整数x,y,使ax+by=d成立。
重要推论:a,b互质的充分必要条件是存在整数x,y使ax+by=1.
n个整数间的裴蜀定理:设a1,a2,a3…an为n个整数,d是它们的最大公约数,那么存在整数x1…xn使得x1a1+x2a2+…xnan=d。
特别来说,如果a1…an存在任意两个数是互质的(不必满足两两互质),那么 存在整数x1… xn使得x1a1+x2a2+ …xnan=1。证法类似两个数的情况。
//求解:ax+by=gcd(a,b);
int extgcd(int a,int b,int &x,int &y)
{
int res=a;
if(b!=0)
{
res=extgcd(b,a%b,y,x);
y-=(a/b)*x;
}
else x=1,y=0;
return res;
}
求得一组x,y后,可通过: (k为任意整数)
x’=x+(b/gcd)k
y’=y-(a/gcd)k
求得全部解。
在此基础上求解ax+by=c(c一定为gcd的整数倍)的全部解
x’=x(c/gcd)+(b/gcd)k
y’=y(c/gcd)-(a/gcd)k
注意:在ax+by=gcd的全部解的基础上都乘以 c/gcd会漏解。
2.求逆元
时间复杂度:O(logn),适用于mod很大,计算个数不多。
//求逆元
int extgcd(int a,int b,int &x,int &y)
{
int res=a;
if(b!=0)
{
res=extgcd(b,a%b,y,x);
y-=(a/b)*x;
}
else x=1,y=0;
return res;
}
ll getan(ll a)
{
ll x,y;
ll s=extgcd(a,mod,x,y);
return s==1?(x%mod+mod)%mod:-1;
}
3.求同余式的解
同余定理:给定一个正整数m,如果两个整数a和b满足(a-b)能够被m整除即(a-b)/ m得到一个整数,那么就称整数a与b对模m同余,记作 a≡b(mod m)。(就相当于a%m=b%m)
消去律:若 gcd(c,p) = 1,则 ac ≡ bc mod p => a ≡ b mod p
反身性: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),b≡c(mod m),则a ± c≡b ± d(mod m)
同余式相乘:若a≡b(mod m),b≡c(mod m),则ac≡bd(mod m)
除法:若ac ≡ bc (mod m) c≠0 则 a≡ b (mod m/gcd(c,m)) 其中gcd(c,m)表示c,m的最大公约数。特殊地 ,gcd(c,m)=1 则a ≡ b (mod m)
幂运算:
如果a ≡ b (mod m),那么a^n ≡ b^n (mod m)
若a ≡ b (mod m),n|m,则 a ≡ b (mod n)
若a ≡ b (mod mi) (i=1,2…n) 则 a ≡ b (mod [m1,m2,…mn]) 其中[m1,m2,…mn]表示m1,m2,…mn的最小公倍数
a x ≡ c (mod m)—>(ax-c)%m=0
则存在 ax-c=my,令y=-y -->c=ax+my会到上述问题1
三.素数
1.基础算法(素性测试,约数枚举,整数分解)
//时间复杂度均为O(log(x))
//素性测试
bool isprime(int x)
{
if(x<=1)return false;
for(int i=2;i*i<=x;i++)
{
if(x%i==0)return false;
}
return true;
}
//约数枚举
void getyueshu(int x)
{
vector<int>a;
for(int i=1;i*i<=x;i++)
{
if(x%i==0)
{
a.push_back(i);
if(x/i!=i)a.push_back(x/i);
}
}
for(int i=0;i<a.size();i++)cout<<a[i]<<" ";
cout<<"\n";
}
//整数分解
void getnum(int x)
{
vector<int>a;
for(int i=2;i*i<=x;i++)
{
while(x%i==0)
{
a.push_back(i);
x=x/i;
}
if(x==1)break;
}
if(x!=1)a.push_back(x);
for(int i=0;i<a.size();i++)cout<<a[i]<<" ";
cout<<"\n";
}
2.素数筛
//时间复杂度O(n*loglogn)
int prime[1000010];
bool isprime[1000010];
int sieve(int n)
{
int cnt=0;
for(int i=0;i<=n;i++)isprime[i]=true;
for(int i=2;i<=n;i++)
{
if(isprime[i])
{
prime[cnt++]=i;
for(int j=i*2;j<=n;j+=i)isprime[j]=false;
}
}
return cnt;
}
//时间复杂度O(n)
int prime[1000010];
bool isprime[1000010];
int sieve(int n)
{
memset(isprime,true,sizeof(isprime));
int cnt=0;
for(int i=2;i<=n;i++)
{
if(isprime[i])prime[cnt++]=i;
for(int j=0;j<cnt&&prime[j]*i<=n;j++)
{
isprime[prime[j]*i]=false;
if(i%prime[j]==0)break;
}
}
return cnt;
}
//区间筛法
bool isprime1[1000010];
bool isprime2[1000010];
int sieve(ll a,ll b)
{
memset(isprime1,true,sizeof(isprime1));
memset(isprime2,true,sizeof(isprime2));
int cnt=0;
for(ll i=2;i*i<b;i++)
{
if(isprime1[i])
{
for(ll j=2*i;j*j<b;j+=i)isprime1[j]=false;
for(ll j=max(2LL,(a+i-1)/i)*i;j<b;j+=i)isprime2[j]=false;
}
}
for(int i=2;i<b-a;i++)if(isprime2[i])cnt++;
return cnt;
}
四.快速幂取模
//时间复杂度O(log n)
ll modpow(ll x,ll n,ll mod)
{
ll res=1;
while(n>0)
{
if(n&1)res=res*x%mod;
x=x*x%mod;
n>>=1;
}
return res;
}
//时间复杂度O(log n)
ll modpow(ll x,ll n,ll mod)
{
if(n==0)return 1;
ll res=modpow(x*x%mod,n/2,mod);
if(n&1)res=res*x%mod;
return res;
}
五.欧拉函数
欧拉函数:就是对于一个正整数n,小于n且和n互质的正整数(包括1)的个数,记作φ(n) 。
欧拉函数的通式:φ(n)=n(1-1/p1)(1-1/p2)(1-1/p3)(1-1/p4)……(1-1/pn)
其中p1, p2……pn为n的所有质因数,n是不为0的整数。φ(1)=1(唯一和1互质的数就是1本身)。
欧拉函数为积性函数,即:若m,n互质, φ ( m n ) = φ ( m ) ⋅ φ ( n ) 。
//欧拉函数计算
ll eular(ll n)
{
ll res=n;
for(int i=2;i*i<=n;i++)
{
if(n%i==0)
{
res=res/i*(i-1);
while(n%i==0)n=n/i;
}
if(n==1)break;
}
if(n>1)res=res/n*(n-1);
return res;
}
//欧拉函数值打表 埃氏筛
int phi[1000010];
void euler_excel(int n)
{
phi[1]=1;
for(int i=2;i<=n;i++)phi[i]=0;
for(int i=2;i<=n;i++)
{
if(!phi[i])
{
for(int j=i;j<=n;j+=i)
{
if(!phi[j])phi[j]=j;
phi[j]=phi[j]/i*(i-1);
}
}
}
}