主要目的就是总结下这几天做的数论题
小知识点
1.最大公约数
int gcd(int a.int b)
{
return b?gcd(b,a%b):a;//辗转相除法
}
2.最小公倍数
int lcm(int a,int b)
{
return a*b/gcd(a,b);
}
素数相关
1. 埃式筛法
从第一个素数开始,把当前素数的整数倍,都打标机标记直到max_size
,这样下一个素数就不会打上标记,让后在用下一个数据当作标准开始标记以后的数字;
void get_primes(int n)//时间复杂度O(nloglogn)
{
for(int i = 2; i <= n; i++)
{
if(!st[i])
{
prime[cnt++] = i;
for(int j = i; j <= n; j += i)
st[j] = true;
}
}
2.欧拉筛法
上面的埃氏筛法,在一定情况下,回重复的标记一些数字,比如 12
,被2
标记的同时也会被3
标记这样回浪费一一定的时间,优化就有了欧拉筛选 ,避免的了某个被多次标记造成时间上的浪费
#include<algorithm>
using namespace std;
int vis[N];//标记数组
int prime[N];//存放素数序列
int num;//1~n的范围内素数的个数
int isprime(int n)
{
for (int i = 2; i <= n; i++)
{
if (!vis[i])
{
prime[num++] = i;
}
for (int j = 0; j < num; j++)
{
if (i * prime[j] > n)
{
break;//防止数组越界
}
vis[i * prime[j]] == 1;
if (i % prime[j] == 0)
{
break;//保证x只会被x的最小质因子筛去
}
}
}
}
欧拉函数
对正整数n,欧拉函数是小于或等于n的正整数中与n互质的数的数目;比如:φ(8) = 4。因为1,3,5,7均和8互质;这里主要用到欧拉函数的三个性质 :
当n为素数时φ(n) = n-1;
当m,n互质时 φ(mn) = φ(m)φ(n);
当a为质数,b % a=0,phi[a*b]=phi[b]*a;
欧拉打表
//数据范围到1e9就死了QAQ
#include<algorithm>
using namespace std;
int vis[N];
int prime[N];
int num;
int phi[N];
//从上面的欧拉筛法修改得到的代码
void eular(int n)
{
phi[1] = 1;//互质要从一开始
for (int i = 2; i <= n; i++)
{
if (!vis[i])
{
prime[++num] = i;//注意++num
phi[i] = i - 1;///特性1
}
for (int j = 1; j <= num && (i*prime[j] < N); j++)
{
vis[i * prime[j]] = 1;
if (i % prime[j] == 0)
{
phi[i * prime[j]] = prime[j] * phi[i];//特性3
break;
}
else
{
phi[i * prime[j]] = (prime[j] - 1) * phi[i];//特性2
//prime[j] - 1 就是 phi[prime[j]];利用了特性1
}
}
}
}
欧拉函数
欧拉函数的通式:φ(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本身)。
int euler(int n)
{
int res = n;
for (int i = 2; i * i <= n; i++)
{
if (n % i == 0)//找到n的最小的质因子
{
res -= res / i;
while (n % i == 0)
{//完全消去这个质因子
n /= i;
}
}
}
if (n > 1)
{
res -= res / n;//最后有可能出先一个未除的因子
}
return ret;//返回结果
}
快速幂
一般情况下有三个数,a, b, p
求a ^ b % p
可以老老实实的这样写:
int pow (int a.int b,int p)//
{
int res = 1;
while (b--)
{
res *= a;
}
res %= p;
return res;
}
时间复杂度O(n)
,而且只能计算一些比较小的数据,但是我们搞算法竞赛的哪有老实人,所以就发明了快速幂算法;还有一些前备知识
用这种算法可以把使劲按复杂度降到O(logn)
还可以计算1e9
的数据;
#include <iostream>
using namespace std;
typedef long long ll;
const int N = 1e6 + 5;
ll ksm(int a, int b, int p)
{
ll res = 1;
a %= p;
while (b)
{
if (b & 1)
{
res = (res * a) % p;
}
a = (a * a) % p;
b >>= 1;
}
return res;
}
扩展欧几里得算法
给出两个整数a,b
扩展欧几里得算法不仅能找a,b
的最大的公约数,还能找到两个整数x,y
使得ax + by = gcd(a,b)
;
没看懂 以后补
除法取模与逆元
没看懂 以后补