欧几里德算法
即求两个数的最大公因子
int GCD(int a,int b)
{
if(b == 0) return a;
else return GCD(b,a % b);
}
扩展欧几里德算法
它可以用来求解(a,b,c为整数)的方程的一组整数解,事实上,只有
时,此方程才有整数解。具体实现
ll exgcd(ll a,ll b,ll& x,ll& y)
{
ll d = a;
if(b != 0) {
d = exgcd(b,a % b,y,x);
y -= (a / b) * x;
}
else {
x = 1,y = 0;
}
return d;
}
根据扩展欧几里德求ax+by=c的最小正整数解,先得到的解
,如果c能整除
则表明该方程有解,
,最小正整数解为(x1 % (b / gcd(a,b) + b / gcd(a,b)) % (b / gcd(a,b))。
线性筛素数
时间复杂度O(n)
#include <stdio.h>
#include <string.h>
#define maxn 100005
bool isPrime[maxn];
int prime[maxn];
void init()
{
int i,j,cnt;
cnt = 0;
memset(isPrime,true,sizeof(isPrime));
isPrime[0] = isPrime[1] = false;
for(i = 2; i < maxn; i++) {
if(isPrime[i])
prime[cnt++] = i;
for(j = 0; j < cnt && i * prime[j] < maxn; j++) {
isPrime[i * prime[j]] = false;
if(i % prime[j] == 0)
break;
}
}
}
int main(void)
{
init();
return 0;
}
唯一分解定理
每一个数都可以表示为,
都是素数,由此可以得出一个数的因子数为
#include <bits/stdc++.h>
using namespace std;
map<int,int> prime_factor(int n)
{
int i;
map<int,int> res;
for(i = 2; i * i <= n; i++) {
while(n % i == 0) {
res[i]++;
n /= i;
}
}
if(n != 1)
res[n] = 1;
return res;
}
int main(void)
{
map<int,int> res = prime_factor(130);
map<int,int>::iterator it;
for(it = res.begin(); it != res.end(); it++) {
printf("%d %d\n",it -> first,it -> second);
}
return 0;
}
欧拉函数
对正整数n,欧拉函数是小于n的正整数中与n互质的数的数目,互质即
,
两个数的公因子只有1。
下面代码即筛出来了素数,也求出了欧拉。
#include <stdio.h>
#include <string.h>
const int maxn = 10000000;
bool isPrime[maxn + 10];
int phi[maxn + 10];
int prime[maxn + 10];
int tot;
void init(int N)
{
int i,j;
memset(isPrime,true,sizeof(isPrime));
isPrime[0] = isPrime[1] = false;
tot = 0;
phi[1] = 1;
for(i = 2; i <= N; i++) {
if(isPrime[i]) {
prime[tot++] = i;
phi[i] = i - 1;
}
for(j = 0; j < tot; j++) {
if(i * prime[j] > N)
break;
isPrime[i * prime[j]] = false;
if(i % prime[j] == 0) {
phi[i * prime[j]] = phi[i] * prime[j];
break;
}
else
phi[i * prime[j]] = phi[i] * (prime[j] - 1);
}
}
}
int main(void)
{
int i;
init(105);
for(i = 0; i <= 105; i++)
printf("%d %d\n",i,phi[i]);
return 0;
}
求单个数的欧拉
ll eular(ll n)
{
int i;
ll ans = n;
for(i = 2; i * i <= n; i++) {
if(n % i == 0) {
ans -= ans / i;
while(n % i == 0)
n /= i;
}
}
if(n > 1) ans -= ans / n;
return ans;
}
乘法逆元
如果,且
,则称a关于模p的乘法逆元为x
,
关于模p的乘法逆元
求逆元三种方法
费马小定理
若p为素数,则有
就是a在mod p意义下的逆元
代码用快速幂实现
ll q_pow(ll a,ll b,ll mod)
{
ll res,base;
res = 1;
base = a;
while(b) {
if(b & 1)
res = (res * base) % mod;
base = (base * base) % mod;
b >>= 1;
}
return res;
}
ll getInv(ll a,ll mod)
{
return q_pow(a,mod - 2,mod);
}
用扩展欧几里德求乘法逆元:
代码实现
ll exgcd(ll a,ll b,ll& x,ll& y)
{
ll d = a;
if(b != 0) {
d = exgcd(b,a % b,y,x);
y -= (a / b) * x;
}
else {
x = 1,y = 0;
}
return d;
}
ll getInv(ll a,ll mod)
{
ll x,y;
ll d = exgcd(a,mod,x,y);
return d == 1 ? (x % mod + mod) % mod : -1;
}
递推求逆元
应用于求组合数
LL fac[MAXN],facinv[MAXN];
LL qpow(LL a, LL b)
{
LL res = 1;
while(b) {
if(b & 1) res = (res * a) % MOD;
a = (a * a) % MOD;
b >>= 1;
}
return res;
}
void init()
{
fac[0] = 1;
for(int i = 1; i < MAXN; i++)
fac[i] = fac[i - 1] * i % MOD;
facinv[MAXN - 1] = qpow(fac[MAXN - 1], MOD - 2);
for(int i = MAXN - 1; i > 0; i--)
facinv[i - 1] = facinv[i] * i % MOD;
}
LL C(LL n, LL m)
{
if(n < 0 || m < 0 || m > n)
return 0;
return fac[n] * facinv[m] % MOD * facinv[n - m] % MOD;
}