算法竞赛笔记——数学
数学
组合数学
1.球盒模型
(1)\(n\) 个 相同的球放入 \(k\) 个不同的盒子里,盒子允许为空:方案数 = \(\binom{n + k - 1}{k - 1}\) = \(\binom{n + k - 1}{n}\)
例如:\(x_1 + x_2 + ... + x_k = n\) 中非负整数解的组数 (隔板法)
(2)\(n\) 个 相同的球放入 \(k\) 个不同的盒子里,盒子不允许为空:方案数 = \(\binom{n - 1}{k - 1}\)
例如:\(x_1 + x_2 + ... + x_k = n\) 中正整数解的组数 (隔板法)
2.第二类斯特林数 \(O(k \cdot log(n))\)
有 \(n\) 个互不相同的球放入 \(k\) 个相同的盒子里,盒子不允许为空:
记号:\(S_2(n,k)\) 或 \({n \brace k}\)
递推式 :\(f(n, k) = k * f(n - 1, k) + f(n - 1, k - 1)\)
容斥:$S_2(n, k) = \frac{1}{k!} \sum_{i=0}^k (-1)^i \binom{k}{i} (k - i)^n $
证明:设事件 \(A_i\) 表示第 \(i\) 个盒子 没有球的方案数
则表示为 \(N((1-A_1)\ldots(1-A_k))\) = \(N((1 - A_1) \ldots (1 - A_k)) = \sum_{i=0}^k (-1)^i \binom{k}{i} (k - i)^n = S_2(n, k) \cdot k!\)
即 $S_2(n,k)= \frac{1}{k!} \sum\limits_{i=0}^n (-1)^i \cdot \binom{k}{i} \cdot (k - i)^n $
重要的公式:$n^m = \sum\limits_{k=0}^m {n \brace k} n^{\underline{k}} $
其中 \(n^{\underline{k}} = n(n-1)(n-2)\ldots(n-k+1)\) 表示 \(n\) 的 \(k\) 次下降幂(下降阶乘)
3.k-MEX
在 \(0\) 到 \(n - 1\) 这 \(n\) 个整数中随机选择 \(k\) 个不同的整数,组成的集合的 \(MEX\) 的期望是 \(\frac{\sum_{i=1}^k i \cdot \binom{n-i-1}{k-i}}{\binom{n}{k}} = \frac{k \cdot \binom{n}{k-1}}{\binom{n}{k}} = \frac{k}{n - k + 1}\)
容斥原理
\(|S - \bigcup\limits_{i=0}^n A_i| = \sum\limits_{i=0}^n(-1)^i \cdot \sum\limits_{1 \leq j_1<j_2<...<j_i \leq n}|{\bigcap\limits_{k=1}^iA_{j_k}}|\)
余数定理
\(N \ \% \ i = N - \lfloor N\ / \ i \rfloor \cdot i\)
/*
考虑整除分块
*/
vector<int> L, R, D, a; //区间左右端点,等差数列公差,等差数列首项
int tot = 0; // 总块数
for(int l = 1, r; l <= n; l = r + 1){
r = n / (n / l); // 根据区间左端点算区间右端点!
L.push_back(l);
R.push_back(r);
D.push_back(n / l);
a.push_back(n % r);
tot ++;
}
线性基
#include <bits/stdc++.h>
using namespace std;
using u64 = unsigned long long;
struct LB {
const int bit = 63;
vector<u64> a = vector<u64>(bit + 1, 0);
bool insert(u64 x){
for(int i = bit; ~i; --i){
if(!((x >> i) & 1)) continue;
if(!a[i]){ a[i] = x; return true; }
x ^= a[i];
}
return false;
}
// 贪心法
u64 query_max(){
u64 ans = 0;
for(int i = bit; ~i; --i){
ans = max(ans, ans ^ a[i]);
}
return ans;
}
void normal(){
for(int i = 0; i <= bit; i ++){
for(int j = i + 1; j <= bit; j ++){
if(a[j] >> i & 1){
a[j] ^= a[i];
}
}
}
}
u64 query_max_k(int k){
k --;
u64 ans = 0;
for(int i = 0; i <= bit; i ++){
if(k >> i & 1){
ans ^= a[i];
}
}
return ans;
}
vector<u64> b = vector<u64>(bit + 1, 0);
void fennec(){ // 正交线性基
normal();
for(int i = 0; i <= bit; i ++){
if(!a[i]){
b[i] = (u64)1 << i;
for(int j = i + 1; j <= bit; j ++){
if(a[j] >> i & 1){
b[i] |= (u64)1 << j;
}
}
}
}
}
};
可持久化线性基
using u64 = unsigned long long;
struct LB {
const int bit = 63;
vector<u64> a = vector<u64>(bit + 1, 0);
vector<int> v = vector<int>(bit + 1, 0);
bool insert(u64 x, int t){
for(int i = bit; ~i; --i){
if(!((x >> i) & 1)) continue;
if(!a[i]){ a[i] = x; return true;}
x ^= a[i];
if(v[i] < t){ a[i] ^= x; swap(v[i], t);}
}
return false;
}
// 贪心法
u64 query_max(int t){
u64 ans = 0;
for(int i = bit; ~i; --i){
if(t <= v[i]){
ans = max(ans, ans ^ a[i]);
}
}
return ans;
}
};
实数线性基
using ld = long double;
const ld eps = 1e-5;
inline void solve() {
int n, m;
cin >> n >> m;
vector<vector<ld>> v(n + 1, vector<ld>(m + 1));
for(int i = 1; i <= n; i ++){
for(int j = 1; j <= m; j ++){
cin >> v[i][j];
}
}
vector<int> cost(n + 1);
for(int i = 1; i <= n; i ++){
cin >> cost[i];
}
vector<int> vis(n + 1);
int ans = 0, tot = 0;
for(int i = 1; i <= m; i ++){
int mi = INF, idx = -1;
for(int j = 1; j <= n; j ++){
if(vis[j]) continue;
if(fabs(v[j][i]) > eps && cost[j] < mi){
mi = cost[j];
idx = j;
}
}
if(idx == -1) continue;
vis[idx] = 1;
ans += mi;
tot ++;
for(int j = 1; j <= n; j ++){
if(vis[j]) continue;
ld k = v[j][i] / v[idx][i];
for(int t = 1; t <= m; t ++){
v[j][t] -= k * v[idx][t];
}
}
}
cout << tot << ' ' << ans << '\n';
}
欧拉函数
定义:欧拉函数 \(\varphi(n)\) 表示 \(区间[1,n]\) 中与 \(n\) 互质的数的个数。例如 \(\varphi(1) = 1\);当 \(n\) 是质数时,显然有 \(\varphi(n)=n-1\)
性质 \(1\):欧拉函数是积性函数,即对于任意满足 \(gcd(a, b) = 1\) 的整数 \(a, b\),有 \(\varphi(ab)=\varphi(a)\varphi(b)\) ,特别地,当 \(n\) 是奇数时 \(\varphi(2n)=\varphi(n)\)。
性质 \(2\):\(n = \sum\limits_{d|n} \varphi(d)\)
性质 \(3\):若 \(n = p^k\),其中 \(p\) 是质数,那么 \(\varphi(n) = p^k - p^{k-1}\)
性质 \(4\):由唯一分解定理,设 \(n = {p_1}^{\alpha_1}{p_2}^{\alpha_2}...{p_k}^{\alpha_k}\) ,其中 \(p_i\) 为质数,有 \(\varphi(n)=n(1 - \frac{1}{p_1})(1 - \frac{1}{p_2})...(1 - \frac{1}{p_k})\)
性质 \(5\):对任意不全为 \(0\) 的整数 \(m,n\),\(\varphi(mn)\varphi(gcd(m,n))=\varphi(m)\varphi(n)gcd(m,n)\)
*补充:\(n\) 的正约数个数 = \(\prod\limits_{i=1}^k (\alpha_i+1)\)
欧拉定理:若 \(a\) 与 \(b\) 互素,即 \(\gcd(a, b) = 1\),且 \(b \ge 1\),则 \(a^{\varphi(b)} \equiv 1 \pmod{b}\)
欧拉筛(线性筛)\(O(n)\)
vector<int> primes;
vector<bool> not_prime;
void get_primes(int n){
not_prime.resize(n + 1);
for(int i = 2; i <= n; i ++){
if(!not_prime[i]) primes.push_back(i);
for(int p : primes){
if(p > n / i) break;
not_prime[p * i] = true;
if(i % p == 0) break;
}
}
}
除数分解性质
设 \(p\) 和 \(q\) 是互质的正整数(即 \(\gcd(p, q) = 1\)),则 \(n=p \cdot q\) 的每个正除数 \(d\) 可唯一表示为:\(d = d_1 \cdot d_2\),
其中 \(d_1 \mid p\) (\(d_1\) 是 \(p\) 的正除数); \(d_2 \mid q\) (\(d_2\) 是 \(q\) 的正除数)则 \(\sum\limits_{d|n}d = \sum\limits_{d|pq}d = \sum\limits_{d_1|p} \sum\limits_{d_2|q}d_1d_2=(\sum\limits_{d_1|p}d_1)(\sum\limits_{d_2|q}d_2)\)
可用此证明:若 \(f\) 为积性函数,则 \(g(n) = \sum\limits_{d \mid n}f(d) = \sum\limits_{d_1 \mid p} \sum\limits_{d_2 \mid q}f(d_1d_2) = \sum\limits_{d_1 \mid p} \sum\limits_{d_2|q}f(d_1)f(d_2) = (\sum\limits_{d_1 \mid p}f(d_1))(\sum\limits_{d_2 \mid q}f(d_2)) = g(p)g(q)\) 也是积性函数
积性函数
定义:如果函数 \(f : N \to R\) ,满足对于任意一对互质的正整数 \(p, q\),都有 \(f(pq)=f(p)f(q)\),则称 \(f\) 为积性函数。
(\(f(1) = 1\))特别地,如果函数 \(f\) 为完全积性函数,则 \(p, q\) 无需互质
例如:(1)欧拉函数 \(\varphi(n)\) 表示 \([1,n]\) 中与 \(n\) 互质的数的数量
(2)\(\tau(n)\) 或 \(d(n)\) 表示 \(n\) 的正因子的数量
(3)\(\sigma(n) % 小写希腊字母 sigma\) 表示 \(n\) 的约数之和
(4)莫比乌斯函数 $\mu(n) $
(5)单位函数 \(\varepsilon(n)\) (完全积性函数)
(6)幂函数 \(f(n) = n^N\) (完全积性函数)
(7)常数函数 \(f(n) = 1\) (完全积性函数)
性质:如果 \(f(n),g(n)\) 是积性函数,则 \(h(n)=f(n)g(n)\) 也是积性函数
结论:如果 \(f(n)\) 是积性函数,则 \(g(n)=\sum\limits_{d \mid n}f(d)\)(其中 \(d\) 是 \(n\) 的正因子)也是积性函数
计算法:假设 \(n = {p_1}^{\alpha_1}{p_2}^{\alpha_2}...{p_k}^{\alpha_k}\),则 \(f(n)=f({p_1}^{\alpha_1})f({p_2}^{\alpha_2})...f({p_k}^{\alpha_k})\)
/*
利用质因数分解求 f(n)
*/
int calc_f(int p, int a){ // 计算 f[p^a], 根据规则自定义(以d(n)为例)
return a + 1;
}
i64 get_f(i64 n){
if(n <= 1) return 1;
i64 ans = 1;
for(int i = 2; i <= n / i; i ++){
int cnt = 0;
while(n % i == 0) cnt ++, n /= i;
ans *= calc_f(i, cnt); // f(p, k) = f(p^k)
}
if(n > 1) ans *= calc_f(n, 1);
return ans;
}
/*
利用欧拉筛预处理 f(1),f(2),...,f(n)
*/
vector<int> f, cnt;
vector<int> primes;
vector<bool> not_prime;
int calc_f(int p, int a){ // 计算 f[p^a], 根据规则自定义(以d(n)为例)
return a + 1;
}
void get_fn(int n){
f.resize(n + 1);
cnt.resize(n + 1);
not_prime.resize(n + 1);
f[1] = 1;
for(int i = 2; i <= n; i ++){
if(!not_prime[i]) primes.push_back(i), f[i] = calc_f(i, 1), cnt[i] = 1;
for(int p : primes){
if(p > n / i) break;
not_prime[p * i] = true;
if(i % p == 0){
cnt[i * p] = cnt[i] + 1;
f[i * p] = f[i] / calc_f(p, cnt[i]) * calc_f(p, cnt[i] + 1);
break;
}
cnt[i * p] = 1;
f[i * p] = f[i] * f[p];
}
}
}
莫比乌斯反演
定理 \(1\):设 $f : N \to R $ ,\(g : N \to R\) 是两个函数,则 \( f(n) = \sum_{d \mid n} g(d) \ \Leftrightarrow \ g(n) = \sum_{d \mid n} \mu\left(\frac{n}{d}\right) f(d) = \sum_{d \mid n} \mu\left(d\right) f(\frac{n}{d}) \)
或者 \(f(n) = \sum_{n \mid d} g(d) \ \Leftrightarrow \ g(n) = \sum_{n \mid d} \mu\left(\frac{d}{n}\right) f(d)\)
定理 \(2\):设 \(f:N \to R ,g:N \to R\) 是两个函数,且存在正整数 \(N\),对于所有的 \(n \gt N,f(n)=g(n)=0\)
则 \( f(n) = \sum_{\substack{n \mid m \\ m \leq N}} g(m) \ \Leftrightarrow \ g(n) = \sum_{\substack{n \mid m \\ m \leq N}} \mu\left(\frac{m}{n}\right) f(m) \)
性质:\( \sum_{d \mid n} \mu(d) = \begin{cases} 1 & n = 1, \\\\ 0 & n \neq 1 \end{cases} \) ,即 \(\sum_{d \mid n} \mu(d) = \varepsilon(n), \quad \mu * 1 = \varepsilon\)
这个性质意味着,莫比乌斯函数在狄利克雷生成函数中,等价于黎曼函数 \(\zeta(s) = \sum_{n=1}^{\infty} \frac{1}{n^s}\) 的倒数。所以在狄利克雷卷积中,莫比乌斯函数是常数函数 \(1\) 的逆元。
其中 \( \mu(n) = \begin{cases} 1 & \text{若 } n = 1, \\\\ 0 & \text{若 } n \text{ 含有平方因子}, \\\\ (-1)^k & \text{若 } k \text{ 为 } n \text{ 的本质不同质因子个数}. \end{cases} \) 为莫比乌斯函数(积性函数)
\(\varepsilon(n) = \begin{cases} 1 & n = 1, \\\\ 0 & n \neq 1 \end{cases}\) 为单位函数
补充结论(反演结论):\([gcd(i,j) = 1] = \varepsilon(\gcd(i,j)) = \sum_{d \mid \gcd(i,j)} \mu(d)\)
/*
利用欧拉筛预处理 f(1),f(2),...,f(n)
*/
vector<int> f;
vector<int> primes;
vector<bool> not_prime;
void get_Mu(int n){
f.resize(n + 1);
not_prime.resize(n + 1);
f[1] = 1;
for(int i = 2; i <= n; i ++){
if(!not_prime[i]) primes.push_back(i), f[i] = -1;
for(int p : primes){
if(p > n / i) break;
not_prime[p * i] = true;
if(i % p == 0){
f[i * p] = 0;
break;
}
f[i * p] = f[i] * f[p];
}
}
}
杂项
基姆拉尔森公式
const int d[]= {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
bool isLeap(int y) {
return y % 400 == 0 || (y % 4 == 0 && y % 100 != 0);
}
int daysInMonth(int y, int m) {
return d[m - 1] + (isLeap(y) && m == 2);
}
int getDay(int y, int m, int d) {
int ans = 0;
for (int i = 1970; i < y; i++) {
ans += 365 + isLeap(i);
}
for (int i = 1; i < m; i++) {
ans += daysInMonth(y, i);
}
ans += d;
return (ans + 2) % 7 + 1;
}
等概率交换排列的两个元素,不动点个数的期望
对于一个初始为 \(1,2,3,...,n\) 的长度为 \(n\) 的排列,每次等概率交换两位,交换 \(k\) 次,则不动点个数的期望 \(E(X) = 1 + (n−1)(\frac{n^2-2n-2}{n^2})^k\),其中不动点定义为交换前后保持原位的元素

浙公网安备 33010602011771号