质数
分解质因数
// 分解质因数,并按照质因数从小到大的顺序输出每个质因数的底数和指数。
// 设x=sqrt(n),则n的大于x的质因数最多有一个,因此时间复杂度为O(N^(1/ 2)),最后进行特判即可
void solve(int x)
{
for(int i = 2; i <= x / i; i ++)
if(x % i == 0)
{
int cnt = 0;
while(x % i == 0)
{
cnt ++;
x /= i;
}
printf("%d %d\n", i, cnt);
}
if(x > 1) printf("%d %d\n", x, 1);
}
线性筛
// O(N)的时间复杂度内获取[1,n]的所有素数
// 求积性函数,例如欧拉函数
void get_primes(int n)
{
for(int i = 2; i <= n ; i ++)
{
// 未被筛过即为质数
if(!st[i]) primes[cnt ++] = i;
for(int j = 0; primes[j] * i <= n; j ++)
{
//筛除以primes[j]为最小质因子的合数
st[primes[j] * i] = true;
// 1、当出现 i % primes[j] == 0 时,primes[j] 一定是 i
// 的最小质因子,因此也一定是 primes[j] * i 的最小质因子。
// 2、当出现 i % primes[j] != 0 时,说明我们还尚未枚举到 i
// 的任何一个质因子,也就表示 primes[j] 小于 i 的任何一个
// 质因子,这时 primes[j] 就一定是 primes[j] * i 的最小质因子。
if(i % primes[j] == 0) break;
}
}
}
约数
求约数
void solve(int x)
{
cnt = 0;
for(int i = 1; i <= x / i; i ++)
{
if(x % i == 0) factors[cnt ++] = i;
if(x % (x / i) == 0 && i != x / i) factors[cnt ++] = x / i;
}
}
约数个数
// 约数个数为所有质因子的幂+1的乘积
unordered_map<int, int> factor_cnt;
void solve(int x)
{
for(int i = 2; i <= x / i; i ++)
while(x % i == 0)
{
factor_cnt[i] ++;
x /= i;
}
if(x > 1) factor_cnt[x] ++;
int res = 1;
for(auto it : factor_cnt) res = (LL)res * (it.second + 1) % MOD;
}
约数之和
unordered_map<int, int> factor_cnt;
void solve(int x)
{
for(int i = 2; i <= x / i; i ++)
while(x % i == 0)
{
factor_cnt[i] ++;
x /= i;
}
if(x > 1) factor_cnt[x] ++;
int res = 1;
for(auto it : factor_cnt)
{
int p = it.first, cnt = it.second, sum = 1;
while(cnt --)
{
sum = ((LL)sum * p % MOD + 1) % MOD;
}
res = (LL)res * sum % MOD;
}
}
欧几里得算法
// 求a b的最大公因数
int gcd(int a, int b)
{
return b ? gcd(b, a % b) : a;
}
欧拉函数
1∼N中与N互质的数的个数被称为欧拉函数,记为 ϕ(N)。
若在算数基本定理中,N=p1^a1 * p2^a2 * … * pm^am,则:
ϕ(N) = N×((p1−1)/p1)×((p2−1)/p2)×…×((pm−1)/pm)
公式求欧拉函数
void solve(int x)
{
cnt = 0;
int res = x;
for(int i = 2; i <= x / i; i ++)
if(x % i == 0)
{
factors[cnt ++] = i;
while(x % i == 0) x /= i;
}
if(x > 1) factors[cnt ++] = x;
for(int i = 0; i < cnt; i ++) res = res / factors[i] * (factors[i] - 1);
cout << res << endl;
}
筛法求欧拉函数
void get_ou(int n)
{
ou[1] = 1;
for(int i = 2; i <= n; i ++)
{
if(!st[i])
{
primes[cnt ++] = i;
// i 为素数时,ϕ(i) = (i - 1)
ou[i] = i - 1;
}
for(int j = 0; primes[j] * i <= n; j ++)
{
st[i * primes[j]] = true;
if(i % primes[j] == 0)
{
// primes[j] 为 i * primes[j] 的质因子,但该质因子的指数不为1
// 故ϕ(i * primes[j]) = primes[j] * ϕ(i)
ou[i * primes[j]] = primes[j] * ou[i];
break;
}
else
{
// primes[j] 为 i * primes[j] 的质因子,且该质因子的指数为1
// 故 ϕ(i * primes[j]) = primes[j] * ((primes[j] - 1) / primes[j]) * ϕ(i)
// 即 ϕ(i * primes[j]) = (primes[j] - 1) * ϕ(i)
ou[i * primes[j]] = (primes[j] - 1) * ou[i];
}
}
}
}
倍增法
快速幂
// 求 a ^ p % p
int qmi(int a, int b, int p)
{
int res = 1;
while(b)
{
if(b & 1) res = (LL)res * a % p;
a = (LL)a * a % p;
b >>= 1;
}
return res;
}
龟速乘
// 求 a * b % p
int mul(int a, int b, int p)
{
int res = 0;
while(b)
{
if(b & 1) res = (res + a) % p;
a = (a << 1) % p;
b >>= 1;
}
return res;
}
扩展欧几里得
// x, y 为一个可行解,使得 ax ≡ d (mod b), 即 ax + by = d
// 通解为:
// x = x0 + kb/d
// y = y0 - ka/d
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;
}
中国剩余定理
公式推导
void solve()
{
cin >> n;
LL a1, m1, a2, m2;
LL x;
cin >> a1 >> m1;
for(int i = 0; i < n - 1; i ++)
{
cin >> a2 >> m2;
LL k1, k2;
LL d = exgcd(a1, a2, k1, k2);
if((m2 - m1) % d)
{
puts("-1");
return;
}
k1 *= (m2 - m1) / d;
k1 = (k1 % (a2 / d) + (a2 / d)) % (a2 / d);
m1 = k1 * a1 + m1;
a1 = a1 / d * a2;
}
cout << m1 << endl;
}
公式计算
令:
1.M = m1 * m2 * m3 * … * mn
2.Mi = M / mi
3.ti是Mi关于mi的逆元,即Mi * ti ≡ 1 (mod mi)
则 x = ∑ai * Mi * ti(1 <= i <= n)
int main()
{
cin >> n;
for(int i = 0; i < n; i ++)
{
cin >> m[i] >> a[i];
M *= m[i];
}
LL res = 0;
for(int i = 0; i < n; i ++)
{
LL Mi = M / m[i], ti, y;
exgcd(Mi, m[i], ti, y);
res += Mi * ti * a[i];
}
cout << (res % M + M) % M << endl;
}
高斯消元
int solve()
{
int c = 0, r = 0;
for(; c < n; c ++)
{
int t = r;
// 取当前列最大值,避免取到过小的数
// 如果取到较小的数在归一时可能会溢出
for(int i = t + 1; i < n; i ++)
if(fabs(a[i][c]) > fabs(a[t][c]) )
t = i;
if(fabs(a[t][c]) < 1e-8) continue;
// 交换两行
for(int i = 0; i < n + 1; i ++) swap(a[t][i], a[r][i]);
// 归一
for(int i = n; i >= c; i --) a[r][i] /= a[r][c];
for(int i = r + 1; i < n; i ++)
for(int j = n; j >= c; j --)
a[i][j] -= a[r][j] * a[i][c];
r ++;
}
if(r < n)
{
for(int i = r; i < n; i ++)
if(fabs(a[i][n]) > 1e-8)
return 2; // 无解
return 1; // 无穷多个解
}
// 求解
for(int i = n - 1; i >= 0; i --)
for(int j = i - 1; j >= 0; j --)
{
a[j][n] -= a[j][i] * a[i][n];
a[j][i] = 0;
}
return 0;
}
组合数
杨辉三角形求组合数
// 适用情况:
// 10^7的询问次数 10^3的数据范围
void init()
{
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 - 1] + C[i - 1][j]) % MOD;
}
}
逆元求组合数
// 由费马小定理求逆元
// 适用情况:
// MOD为素数 10^6的数据范围 10^6的询问次数
void init()
{
fact[0] = infact[0] = 1;
for(int i = 1; i < N; i ++)
{
fact[i] = (LL)fact[i - 1] * i % MOD;
infact[i] = (LL)infact[i - 1] * qmi(i, MOD - 2, MOD) % MOD;
}
}
int C(int a, int b)
{
return (LL)fact[a] * infact[b] % MOD * infact[a - b] % MOD;
}
lucas定理求组合数
int C(int a, int b, int p)
{
int res = 1;
for(int i = a, j = 1; j <= b; i --, j ++)
res = (LL) res * i % p * qmi(j, p - 2, p) % p;
return res;
}
// 适用数据范围为10^18,C(a, b)的计算方法可以根据题意选择
LL lucas(LL a, LL b, int p)
{
if(a < p && b < p) return C(a, b, p);
return C(a % p, b % p, p) * lucas(a / p, b / p, p) % p;
}
高精度求组合数
// 适用情况:题目要求使用高精度
int get_factor(int a, int p)
{
int res = 0;
while(a)
{
res += a / p;
a /= p;
}
return res;
}
vector<int> C(int a, int b)
{
for(int i = 0; i < cnt; i ++)
{
int p = primes[i];
cnts[i] = get_factor(a, p) - get_factor(b, p) - get_factor(a - b, p);
}
vector<int> res;
res.push_back(1);
for(int i = 0; i < cnt; i ++)
while(cnts[i])
{
res = mul(res, primes[i]);
cnts[i] --;
}
return res;
}
卡特兰数
void solve()
{
int a = 2 * n, b = n;
int res = C(a, b) / (b + 1);
}
容斥原理
博弈论
异或
sg