数论
数论
一、质数
1、定义:大于1的整数中如果只包含1和本身这两个约数就被称为质数或素数
2、试除法
bool is_prime(int n)
{
if(n < 2) return false;
//不推荐i <= sqrt(n)这样每次都会算一遍sqrt(),也不推荐i*i <= n这样可能会爆int
for(int i = 2; i <= n / i; i ++)
if(n % i == 0)
return false;
return true;
}
时间复杂度:sqrt(n);
3、分解质因数
试除法
思路:从小到大枚举n的所有约数
int a[10010], cnt;
inline void divide(int k)
{
for(int i = 2; i <= k / i; i ++)
if(k % i == 0)
{
while(k % i == 0) k /= i;
m[++ cnt] = i;
}
if(k > 1) m[++cnt] = k;
}
时间复杂度:logn~sqrt(n)当n = 2^k时除k次就会把2除尽此时除了logn次
筛质数
埃氏筛质数
思路:判断p是否为质数并不需要判断2~p - 1是否都能被p整除,只需要判断其中的质数即可
inline void get_prime(int n)
{
for(int i = 2; i <= n; i ++)
{
if(!st[i]) primes[cnt ++] = i;
for(int j = i + i; j <= n; j += i) st[j] = true;
}
}
时间复杂度:n(ln n + c),(其中c为欧拉常数) <nlogn
质数定理 : 1~n中有n/lnn个质数
线性筛质数
n只会被n的最小质因子筛掉
inline void get_prime(int n)
{
for(int i = 2; i <= n; i ++)
{
if(!st[i]) primes[cnt ++] = i;
for(int j = 0; primes[j] <= n / i; j ++)
{
st[primes[j] * i] = true;
if(i % primes[j] == 0) break;
}
}
}
约数
试除法
inline vector<int> get_divide(int n)
{
vector<int> ans;
for(int i = 1; i <= n / i; i ++)
{
if(n % i == 0)
{
ans.push_back(i);
if(n / i != i) ans.push_back(n / i);
}
}
sort (ans.begin(), ans.end());
return ans;
}
时间复杂度:sqrt(n)
约数个数
约数个数是把一个数分解质因数之后所有质因数的指数都加1的乘积之和
#include <iostream>
#include <unordered_map>
using namespace std;
typedef long long ll;
const int mod = 1e9 + 7;
unordered_map<int, int>primes;
int n;
int main()
{
cin >> n;
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] ++;
}
ll ans = 1;
for(auto x : primes) ans = ans * (x.second + 1) % mod;
cout << ans << endl;
return 0;
}
欧拉函数
fai(N):1~N中所有与N互质的数的个数
#include <iostream>
using namespace std;
int main()
{
int n;cin >> n;
while(n --)
{
int x; cin >> x;
int ans = x;
for(int i = 2; i <= x / i; ++i)
{
if(x % i == 0) ans = ans / i * (i - 1);
while(x % i == 0) x /= i;
}
if(x > 1) ans = ans / x * (x - 1);
cout << ans << endl;
}
return 0;
}
线性筛求欧拉函数
#include<iostream>
using namespace std;
const int N = 1e6 + 10;
typedef long long ll;
int primes[N], cnt, phi[N], n;
bool st[N];
ll get_eruler(int n)
{
phi[1] = 1;
for(int i = 2; i <= n; ++ i)
{
if(!st[i])
{
primes[cnt ++] = i;
phi[i] = i - 1;
}
for(int j = 0; primes[j] <= n / i; ++ j)
{
st[primes[j] * i] = true;
if(i % primes[j] == 0)
{
phi[i * primes[j]] = phi[i] * primes[j];
break;
}
phi[i * primes[j]] = phi[i] * (primes[j] - 1);
}
}
ll res = 0;
for(int i = 1; i<= n; ++ i) res += phi[i];
return res;
}
int main()
{
cin >> n;
cout << get_eruler(n);
return 0;
}
快速幂
在o(logk)的时间内求出来a^k mod p的结果
#include <iostream>
using namespace std;
typedef long long ll;
int a, k, p;
int qmi(int a, int k, int p)
{
int res = 1;
while(k)
{
if(k & 1) res = (ll) res * a % p;
k >>= 1;
a = (ll) a * a % p;
}
return res;
}
int main()
{
int n;scanf("%d", &n);
while(n --)
{
scanf("%d%d%d", &a, &k, &p);
printf("%d\n", qmi(a, k, p));
}
return 0;
}
扩展欧几里得
#include <iostream>
using namespace std;
int exgcd(int a, int b, int &x, int &y)
{
if(!b)
{
x = 1; y = 0;
return a;
}
int d = exgcd(b, a % b, y, x);
y -= a / b * x;
return d;
}
int main()
{
int n;scanf("%d", &n);
while(n --)
{
int a, b, x, y;
scanf("%d%d", &a, &b);
exgcd(a, b, x, y);
printf("%d %d\n", x, y);
}
return 0;
}
奇怪的表达式
#include<iostream>
using namespace std;
typedef long long ll;
ll exgcd(ll a, ll b, ll& x, ll& y)
{
if(!b)
{
x = 1, y = 0;
return a;
}
int d = exgcd(b, a % b, y, x);
y -= a / b * x;
return d;
}
int main()
{
int n; cin >> n;
ll m1, a1; cin >> a1 >> m1;
bool st = false;
for(int i = 0; i < n -1 ; ++ i)
{
ll m2, a2;cin >> a2 >> m2;
ll k1, k2;
ll d = exgcd(a1, a2, k1, k2);
if((m2 - m1) % d)
{
st = true;
break;
}
k1 = k1 * (m2 - m1) / d;
ll t = a2 / d;
k1 = (k1 % t + t) % t;
m1 = m1 + k1 * a1;
a1 = abs(a1 * a2 / d);
}
if(!st)
{
cout << (m1 % a1 + a1) % a1 << endl;
}
else cout << -1 << endl;
return 0;
}
高斯消元
组合数
思路:组合数的一个公司递推
公式的推导从i个物品中选j个,可以分为两大类第i个物品选或不选,选的话只需要冲i-1个物品中选j - 1个物品即可,不选的话,从i-1个物品选j个物品
#include <iostream>
using namespace std;
const int N = 2010, mod = 1e9 + 7;
int c[N][N], n;
int main()
{
cin >> n;
for(int i = 0; i < N; ++ i)
for(int j = 0; j <= i; ++ j)
{
if(!j) c[i][j] = 1;
else c[i][j] = (c[i - 1][j] + c[i - 1][j - 1]) % mod;
}
while(n --)
{
int a, b;cin >> a >> b;
cout << c[a][b] << endl;
}
return 0;
}
逆元法求组合数
#include<iostream>
using namespace std;
typedef long long ll;
const int N = 1e5 + 10, mod = 1e9 + 7;
int f[N], inf[N];
ll qmi(int a, int k, int mod)
{
ll res = 1;
while(k)
{
if(k & 1) res = res * a % mod;
a = (ll) a * a % mod;
k >>= 1;
}
return res;
}
int main()
{
f[0] = inf[0] = 1;
for(int i = 1; i < N; ++ i)
{
f[i] = (ll) f[i - 1] * i % mod;
inf[i] = (ll) inf[i - 1] * qmi(i, mod - 2, mod) % mod;
}
int n; cin >> n;
while(n --)
{
int a, b; scanf("%d%d", &a, &b);
ll ans = (ll) f[a] * inf[b] % mod * inf[a - b] % mod;
cout << ans << endl;
}
return 0;
}
卢卡斯定理求组合数
#include<iostream>
using namespace std;
typedef long long ll;
int p;
int qmi(int a, int k)
{
int res = 1;
while(k)
{
if(k & 1) res = (ll) res * a % p;
a = (ll) a * a % p;
k >>= 1;
}
return res;
}
int C(int a, int b)
{
if(b > a) return 0;
ll res = 1;
for(int i = 1, j = a; i <= b; ++ i, --j)
{
res = (ll) res * j % p;
res = (ll) res * qmi(i, p - 2) % p;
}
return res;
}
ll lucas(ll a, ll b, int p)
{
if(a < p && b < p) return C(a, b);
return (ll) lucas(a % p, b % p, p) * lucas(a / p, b / p, p) % p;
}
int main()
{
int n;cin >> n;
while(n --)
{
ll a, b; cin >> a >> b >> p;
cout << lucas(a, b, p) << endl;
}
return 0;
}
卡特兰数
#include<iostream>
using namespace std;
const int mod = 1e9 + 7;
typedef long long ll;
int qmi(int a, int k, int p)
{
int res = 1;
while(k)
{
if(k & 1) res = (ll) res * a % p;
a = (ll) a * a % p;
k >>= 1;
}
return res;
}
int main()
{
int n;cin >> n;
int a = 2 * n, b = n;
int res = 1;
for(int i = a; i > a - b; -- i) res = (ll) res * i % mod;
for(int i = 1; i <= b; ++ i) res = (ll) res * qmi(i, mod - 2, mod) % mod;
res = (ll) res * qmi(n + 1, mod - 2, mod) % mod;
cout << res << endl;
return 0;
}