数论
质数
在大于1的整数中, 如果只包含1和本身这两个约数, 就被称为质数, 或者叫质数
(1)质数的判定——试除法
//试除法判定质数模板
bool is_prime(int x)
{
if(x<2)return false;
for(int i=2;i<=x/i;i++)
if(x%i==0)return false;
return true;
}
注意:循环的结束条件为 i<=x/i ,若改为 i*i<=x 在x接近int上界时会有溢出风险
(2)分解质因数——试除法
//试除法分解质因数模板
void divide(int x)
{
for(int i=2;i<=x/i;i++)
if(x%i==0)
{
int s=0;
while(x%i==0)
{
s++;
x/=i;
}
cout<<i<<' '<<s<<'\n';
}
if(x>1)cout<<x<<' '<<1<<'\n';
//n中最多只含一个大于sqrt(n)的质因子
}
(3)质数的判定——埃氏筛法
//朴素筛法求素数模板
int primes[N],cnt; //primes[]存储所有的素数
bool st[N]; //st[]存储x是否被筛掉
void get_primes(int n)
{
for(int i=2;i<=n;i++)
{
if(st[i])continue;
primes[cnt++]=i;
for(int j=i+i;j<=n;j+=i)
st[j]=true;
}
}
(4)质数的判定——线性筛法
//线性筛法求素数模板
int primes[N],cnt; //primes[]存储所有的素数
bool st[N]; //st[x]存储x是否被筛掉
void get_primes(int n)
{
for(int i=2;i<=n;i++)
{
if(!st[j])primes[cnt++]=i;
for(int j=0;primes[j]<=n/i;j++)
{
st[primes[j]*i]=true;
if(i%primes[j]==0)break;
//primes[j]一定是i的最小质因子
}
}
}
约数
(1)试除法求一个数的所有约数
//试除法求所有的约数模板
vector<int> get_divisors (int x)
{
vector<int>res;
for(int i=1;i<=x/i;i++)
if(x%i==0)
{
res.push_back(i);
if(i!=x/i)res.push_back(x/i);
}
sort(res.begin(),res.end());
return res;
}
(2)约数个数
(3)约数之和
如果 N = \(p^{c_1}_{1}\) × \(p1^{c_2}_{2}\) × \(\cdots\) × \(p^{c_k}_k\)
约数个数: (\(c_1\) + 1) × (\(c_2\) + 1) × \(\cdots\) × (\(c_k\) + 1)
约数之和: (\(p^0_1\) + \(p^1_1\) + \(p^2_1\) + \(\cdots\) + \(p^{c_1}_1\)) × (\(p^0_2\) + \(p^1_2\) + \(p^2_2\) + \(\cdots\) + \(p^{c_2}_2\)) × \(\cdots\) × (\(p^0_k\) + \(p^1_k\) + \(\cdots\) + \(p^{c_k}_k\) )
//求约数个数模板
unordered_map<int,int>primes;
while(n--)
{
int x;
cin>>x;
for(int i=2;i<=x/i;i++)
while(x%i==0)
{
x/=i;
primes[i]++;
}
if(x>1)primes[x]++;
}
long long res=1;
for(auto p:primes)res=res*(p.second+1)%mod;
//求约数之和模板
unordered_map<int,int>primes;
while(n--)
{
int x;
cin>>x;
for(int i=2;i<=x/i;i++)
while(x%i==0)
{
x/=i;
primes[i]++;
}
if(x>1)primes[x]++;
}
long long res=1;
for(auto p:primes)
{
long long a=p.first,b=p.second;
long long t=1;
while(b--)t=(t*a+1)%mod;
res=res*t%mod;
}
(4)欧几里得算法(辗转相除法)
原理: d 能整除 a , d 能整除 b \(\longrightarrow\) 则 d 能整除 ax+by
a % b = a - [ \(\dfrac{a}{b}\) ] × b = a - c × b
a 和 b的最大公约数
= b 和 a - c × b 的最大公约数
= b 和 a % b 的最大公约数
//欧几里得算法
int gcd(int a,int b)
{
return b?gcd(b,a%b):a;
}
欧拉函数
欧拉函数 \(\varphi\) (n) 1 ~ n 中与n互质的数的个数
如果 N = \(p^{\alpha_1}_1\) × \(p^{\alpha_2}_2\) × \(\cdots\) × \(p^{\alpha_k}_k\)
\(\varphi\) (N) = N × (1 - \(\dfrac{1}{p_1}\)) × (1 - \(\dfrac{1}{p_2}\)) × \(\cdots\) × (1 - \(\dfrac{1}{p_k}\))
//公式法求欧拉函数模板
int phi(int x)
{
int res=x;
for(int i=2;i<=x/i;i++)
if(x%i==0)
{
res=res/i*(i-1);
while(x%i==0)x/=i;
}
if(x>1)res=res/x*(x-1);
return res;
}
//筛法求欧拉函数模板
int primes[N],cnt; //primes[]存储所有的素数
int euler[N]; //存储每个数的欧拉函数
bool st[N]; //st[x]存储x是否被筛掉
void get_eulers(int n)
{
eular[1]=1;
for(int i=2;i<=n;i++)
{
if(!st[i])
{
primes[cnt++]=i;
euler[i]=i-1;
}
for(int j=0;primes[j]<=n/i;j++)
{
int t=primes[j]*i;
st[t]=true;
if(i%primes[j]==0)
{
euler[t]=euler[i]*primes[j];
break;
}
euler[t]=euler[i]*(primes[j]-1);
}
}
}
欧拉定理: 若 a 与 n 互质, 则 \(a^{\varphi~(n)~}\) % n = 1
费马定理: 若 a 与 p 互质, p为质数, 则 \(a^{p-1}\) % p = 1
快速幂
快速幂: 用 O(log2k)时间计算出 \(a^k\) mod p
算法原理: 先预处理出
\[\begin{cases} a^{2^0} \text{ mod p}\\[0.3ex] a^{2^1} \text{ mod p} \quad\quad\quad\quad \text{$a^{2^1}$ = $a^{2^{0}*2}$ = $(a^{2^0})^{2}$}\\[0.3ex] a^{2^2} \text{ mod p} \quad\quad\quad\quad \text{$a^{2^2}$ = $a^{2^{1}*2}$ = $(a^{2^1})^{2}$}\\[0.3ex] \quad\quad \cdots \\[0.3ex] a^{2^{log_{2}k}} \text{ mod p} \quad\quad\quad \text{$a^{2^{log_{2}k}}$ = $(a^{2^{log_{2}k}-1})^{2}$}\\[0.3ex] \end{cases} \]\(a^k\) = \(a^{2^{x_1}}\) * \(a^{2^{x_2}}\) \(\cdots\) \(a^{2^{x_t}}\)
= \(a^{2^{x_1}+2^{x_2}+2^{x_3}+\cdots+2^{x_t}}\)
//快速幂模板
//求 (m^k)%p
int qmi(int m,int k,int p)
{
int res=1%p,t=m;
while(k)
{
if(k&1)res=res*t%p;
t=t*t%p;
k>>=1;
}
return res;
}
扩展欧几里得算法
裴蜀定理: 对于任意正整数a, b, 一定存在非零整数x, y, 使得 ax + by = gcd (a, b)
//扩展欧几里得算法模板
//求x,y,使得ax+by=gcd(x,y)
int exgcd(int a,int b,int &x,int &y)
{
if(b==0)
{
x=1,y=0;
return a;
}
int d=exgcd(b,a%b,y,x);
y-=(a/b)*x;
return d;
}