1、gcd():最大公约数
int gcd(a,b){//算法导论上指出a、b为非负整数,不用在意两者的大小关系
if b==0 return a;
else return gcd( b,a % b)
}//时间复杂度O(log max(a,b))
gcd &&lcm:
long long gcd(long long a, long long b)
{ return a == 0 ? b : gcd(b % a, a); }
long long lcm(long long a, long long b)
{ return a * b / gcd(a, b); }
2、ex_gcd():扩展欧几里得算法
一定存在整数对(x,y)使得ax+by=gcd(a,b)。此处省略一万字数学证明
int extgcd(int a,int b,int x, int y){
int d =a;
if(b!=0){
d = extgcd(b,a%b,y,x);
y -= (a/b)*x;
}
else{
x=1;
y=0;
}
return d;//返回的是gcd(a,b) ,时间内复杂度同上
}
3、素数:
1、对于单独判断一个数字是否为素数,只要从2~根号n之间找即可。
2、输出或统计n以内有多少素数时,采用线性筛,复杂度O(n)
int p[NMAX], pn; bool not_prime[NMAX]; void prime() { pn = 0; not_prime[1] = 1; for(int i = 2; i < NMAX; i++) { if(!not_prime[i]) p[pn++] = i; for(int j = 0; j < pn && i * p[j] < NMAX; j++) { not_prime[i * p[j]] = 1; if(i % p[j] == 0) break; } } }
3、求一个区间内的素数个数:
解释一下:因为b内的合数最小质因数一定不消耗国根号b。即先分别做好[2,根号b)的表和[a,b)的表,然后从小的表里筛得素数的同时,将他的倍数从大表中划去即可。
bool is_prime[1000010];
bool is_prime_small[1000010];
void segment_sieve(ll a, ll b)
{
for(int i=0; (ll)i*i<b; i++) is_prime_small[i] = true;//i是素数
for(int i=0; i<b-a; i++) is_prime[i] = true;
//利用0~len代表a~b的数
for(int i=2; (ll)i*i<b; i++)
{
if(is_prime_small[i])
{
for(int j=i+i; (ll)j*j<b; j+=i)
is_prime_small[j] = false; //筛[2,√b)
for(ll j=max(2LL, (a+i-1)/i)*i; j<b; j+=i)
is_prime[j-a] = false; //筛[a,b)
//j代表素数,j-a是将a~b变为0~b-a以便数组好存储
//2LL是2的长整形形式,与其比较意思是j最少是i的两倍
//((a+i-1)/i)*i得出的是(>=a && %i==0)离a最近的数,其实
//也可以写成a%i==0 ? a : (a/i+1)*i
}
}
}