voidquick_sort(int q[], int l, int r){ if (l >= r) return; int i = l - 1, j = r + 1, x = q[l + r >> 1]; while (i < j) { do i++; while (q[i] < x); do j--; while (q[j] > x); if (i < j) swap(q[i], q[j]); } quick_sort(q, l, j), quick_sort(q, j + 1, r); }
voidmerge_sort(int q[], int l, int r){ if (l >= r) return; int mid = l + r >> 1; merge_sort(q, l, mid); merge_sort(q, mid + 1, r); int k = 0, i = l, j = mid + 1; while (i <= mid && j <= r) if (q[i] <= q[j]) tmp[k++] = q[i++]; else tmp[k++] = q[j++];
while (i <= mid) tmp[k++] = q[i++]; while (j <= r) tmp[k++] = q[j++];
for (i = l, j = 0; i <= r; i++, j++) q[i] = tmp[j]; }
// 区间[l, r]被划分成[l, mid]和[mid + 1, r]时使用: intbsearch_1(int l, int r){ while (l < r) { int mid = l + r >> 1; if (check(mid)) r = mid; // check()判断mid是否满足性质 else l = mid + 1; } return l; } // 区间[l, r]被划分成[l, mid - 1]和[mid, r]时使用: intbsearch_2(int l, int r){ while (l < r) { int mid = l + r + 1 >> 1; if (check(mid)) l = mid; else r = mid - 1; } return l; }
浮点数二分
1 2 3 4 5 6 7 8 9 10 11 12 13 14
boolcheck(double x){ /* ... */ } // 检查x是否满足某种性质
doublebsearch_3(double l, double r){ constdouble eps = 1e-6; // eps 表示精度,取决于题目对精度的要求 while (r - l > eps) { double mid = (l + r) / 2; if (check(mid)) r = mid; else l = mid; } return l; }
高精度加法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
// C = A + B, A >= 0, B >= 0 vector<int> add(vector<int>& A, vector<int>& B){ if (A.size() < B.size()) returnadd(B, A);
vector<int> C; int t = 0; for (int i = 0; i < A.size(); i++) { t += A[i]; if (i < B.size()) t += B[i]; C.push_back(t % 10); t /= 10; }
if (t) C.push_back(t); return C; }
高精度减法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
// C = A - B, 满足A >= B, A >= 0, B >= 0 vector<int> sub(vector<int>& A, vector<int>& B){ vector<int> C; for (int i = 0, t = 0; i < A.size(); i++) { t = A[i] - t; if (i < B.size()) t -= B[i]; C.push_back((t + 10) % 10); if (t < 0) t = 1; else t = 0; }
// C = A * b, A >= 0, b >= 0 vector<int> mul(vector<int>& A, int b){ vector<int> C;
int t = 0; for (int i = 0; i < A.size() || t; i++) { if (i < A.size()) t += A[i] * b; C.push_back(t % 10); t /= 10; } while (C.size() > 1 && C.back() == 0) C.pop_back(); return C; }
高精度除以低精度
1 2 3 4 5 6 7 8 9 10 11 12 13 14
// A / b = C ... r, A >= 0, b > 0 vector<int> div(vector<int>& A, int b, int& r){ vector<int> C; r = 0; for (int i = A.size() - 1; i >= 0; i--) { r = r * 10 + A[i]; C.push_back(r / b); r %= b; } reverse(C.begin(), C.end()); while (C.size() > 1 && C.back() == 0) C.pop_back(); return C; }
// 二分求出x对应的离散化的值 // 找到第一个大于等于x的位置 intfind(int x){ int l = 0, r = alls.size() - 1; while (l < r) { int mid = l + r >> 1; if (alls[mid] >= x) r = mid; else l = mid + 1; } return r + 1; // 映射到1, 2, ...n }
int st = -2e9, ed = -2e9; for (auto seg : segs) if (ed < seg.first) { if (st != -2e9) res.push_back({st, ed}); st = seg.first, ed = seg.second; } else ed = max(ed, seg.second);
if (st != -2e9) res.push_back({st, ed}); segs = res; }
int son[N][26], cnt[N], idx; // 0号点既是根节点,又是空节点 // son[][]存储树中每个节点的子节点 // cnt[]存储以每个节点结尾的单词数量
// 插入一个字符串 voidinsert(char* str){ int p = 0; for (int i = 0; str[i]; i++) { int u = str[i] - 'a'; if (!son[p][u]) son[p][u] = ++idx; p = son[p][u]; } cnt[p]++; }
// 查询字符串出现的次数 intquery(char* str){ int p = 0; for (int i = 0; str[i]; i++) { int u = str[i] - 'a'; if (!son[p][u]) return0; p = son[p][u]; } return cnt[p]; }
并查集
朴素并查集
1 2 3 4 5 6 7 8 9 10 11 12 13
// (1) 朴素并查集: int p[N]; //存储每个点的祖宗节点 // 返回x的祖宗节点 intfind(int x){ if (p[x] != x) p[x] = find(p[x]); return p[x]; } // 初始化,假定节点编号是1~n for (int i = 1; i <= n; i++) p[i] = i; // 合并a和b所在的两个集合: p[find(a)] = find(b);
维护size的并查集
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
int p[N], size[N]; // p[]存储每个点的祖宗节点, // size[]只有祖宗节点的有意义,表示祖宗节点所在集合中的点的数量 // 返回x的祖宗节点 intfind(int x){ if (p[x] != x) p[x] = find(p[x]); return p[x]; } // 初始化,假定节点编号是1~n for (int i = 1; i <= n; i++) { p[i] = i; size[i] = 1; } // 合并a和b所在的两个集合: size[find(b)] += size[find(a)]; p[find(a)] = find(b);
维护到祖宗节点距离的并查集
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
int p[N], d[N]; // p[]存储每个点的祖宗节点, d[x]存储x到p[x]的距离 // 返回x的祖宗节点 intfind(int x){ if (p[x] != x) { int u = find(p[x]); d[x] += d[p[x]]; p[x] = u; } return p[x]; } // 初始化,假定节点编号是1~n for (int i = 1; i <= n; i++) { p[i] = i; d[i] = 0; } // 合并a和b所在的两个集合: p[find(a)] = find(b); d[find(a)] = distance; // 根据具体问题,初始化find(a)的偏移量
// 交换两个点,及其映射关系 voidheap_swap(int a, int b){ swap(ph[hp[a]], ph[hp[b]]); swap(hp[a], hp[b]); swap(h[a], h[b]); }
voiddown(int u){ int t = u; if (u * 2 <= size && h[u * 2] < h[t]) t = u * 2; if (u * 2 + 1 <= size && h[u * 2 + 1] < h[t]) t = u * 2 + 1; if (u != t) { heap_swap(u, t); down(t); } }
voidup(int u){ while (u / 2 && h[u] < h[u / 2]) { heap_swap(u, u / 2); u >>= 1; } }
// C[4]=A[1]+A[2]+A[3]+A[4]; // C[5]=A[5]; int c[N], a[N]; intlowbit(int t){ return t & (-t); } // x点增量为y voidupdate(int x, int y){ for (int i = x; i <= n; i += lowbit(i)) c[i] += y; } // 区间求和 [1..x] // i与j区间,用getsum(j)-getsum(i-1)求得 intgetsum(int x){ int ans = 0; for (int i = x; i; i -= lowbit(i)) ans += c[i]; return ans; } // 求逆序对 for (int i = 1; i <= n; i++) { update(b[i] + 1); res += i - getsum(b[i] + 1); }
unordered_map<const Key, T> map; unordered_map<Key,T>::iterator it; (*it).first; // the key value (of type Key) (*it).second; // the mapped value (of type T) (*it); // the "element value" (of type pair<const Key,T>) size() empty() find(k) insert({k,v}) at(k)
1 2 3 4 5 6 7 8 9 10 11
std::unordered_map<std::string, std::int> umap; //定义 umap.insert(Map::value_type("test", 1));//增加 //根据key删除,如果没找到n=0 auto n = umap.erase("test") //删除 auto it = umap.find(key) //改 if(it != umap.end()) it->second = new_value; //map中查找x是否存在 umap.find(x) != map.end()//查 //或者 umap.count(x) != 0
// 如果第n次迭代仍然会松弛三角不等式,就说明存在一条长度是n+1的最短路径,由抽屉原理,路径中至少存在两个相同的点,说明图中存在负权回路。 for (int i = 0; i < n; i++) { for (int j = 0; j < m; j++) { int a = edges[j].a, b = edges[j].b, w = edges[j].w; if (dist[b] > dist[a] + w) dist[b] = dist[a] + w; } }
if (dist[n] > 0x3f3f3f3f / 2) return-1; return dist[n]; }
queue<int> q; for (int i = 1; i <= n; i++) { q.push(i); st[i] = true; }
while (q.size()) { auto t = q.front(); q.pop();
st[t] = false;
for (int i = h[t]; i != -1; i = ne[i]) { int j = e[i]; if (dist[j] > dist[t] + w[i]) { dist[j] = dist[t] + w[i]; cnt[j] = cnt[t] + 1; if (cnt[j] >= n) returntrue; // 如果从1号点到x的最短路中包含至少n个点(不包括自己),则说明存在环 if (!st[j]) { q.push(j); st[j] = true; } } } } returnfalse; }
floyd算法
1 2 3 4 5 6 7 8 9 10 11 12 13 14
初始化: for (int i = 1; i <= n; i ++ ) for (int j = 1; j <= n; j ++ ) if (i == j) d[i][j] = 0; else d[i][j] = INF;
// 算法结束后,d[a][b]表示a到b的最短距离 voidfloyd() { for (int k = 1; k <= n; k ++ ) for (int i = 1; i <= n; i ++ ) for (int j = 1; j <= n; j ++ ) d[i][j] = min(d[i][j], d[i][k] + d[k][j]); }
int n; // n表示点数 int h[N], e[M], ne[M], idx; // 邻接表存储图 int color[N]; // 表示每个点的颜色,-1表示未染色,0表示白色,1表示黑色
// 参数:u表示当前节点,c表示当前点的颜色 booldfs(int u, int c){ color[u] = c; for (int i = h[u]; i != -1; i = ne[i]) { int j = e[i]; if (color[j] == -1) { if (!dfs(j, !c)) returnfalse; } elseif (color[j] == c) returnfalse; }
returntrue; }
boolcheck(){ memset(color, -1, sizeof color); bool flag = true; for (int i = 1; i <= n; i++) if (color[i] == -1) if (!dfs(i, 0)) { flag = false; break; } return flag; }
boolis_prime(int x) { if (x < 2) returnfalse; for (int i = 2; i <= x / i; i ++ ) if (x % i == 0) returnfalse; returntrue; }
分解质因数
1 2 3 4 5 6 7 8 9 10 11 12
voiddivide(int x) { for (int i = 2; i <= x / i; i ++ ) if (x % i == 0) { int s = 0; while (x % i == 0) x /= i, s ++ ; cout << i << ' ' << s << endl; } if (x > 1) cout << x << ' ' << 1 << endl; cout << endl; }
朴素筛法求素数
1 2 3 4 5 6 7 8 9 10 11
int primes[N], cnt; // primes[]存储所有素数 bool st[N]; // st[x]存储x是否被筛掉
voidget_primes(int n){ for (int i = 2; i <= n; i ++ ) { if (st[i]) continue; primes[cnt ++ ] = i; for (int j = i + i; j <= n; j += i) st[j] = true; } }
线性筛素数
1 2 3 4 5 6 7 8 9 10 11 12
int primes[N], cnt; // primes[]存储所有素数 bool st[N]; // st[x]存储x是否被筛掉
voidget_primes(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; } } }
试除法求所有约数
1 2 3 4 5 6 7 8 9 10
vector<int> get_divisors(int x){ vector<int> res; for (int i = 1; i <= x / i; i ++ ) if (x % i == 0) { res.push_back(i); if (i != x / i) res.push_back(x / i); } sort(res.begin(), res.end()); return res; }
intgcd(int a, int b){ return b ? gcd(b, a % b) : a; }
求欧拉函数
1 2 3 4 5 6 7 8 9 10 11 12
intphi(int x){ int res = x; for (int i = 2; i <= x / i; i++) if (x % i == 0) { res = res / i * (i - 1); while (x % i == 0) x /= i; } if (x > 1) res = res / x * (x - 1); return res; }
// 筛法求欧拉函数 —— 模板题 AcWing 874. 筛法求欧拉函数 int primes[N], cnt; // primes[]存储所有素数 int euler[N]; // 存储每个数的欧拉函数 bool st[N]; // st[x]存储x是否被筛掉
voidget_eulers(int n){ euler[1] = 1; for (int i = 2; i <= n; i++) { if (!st[i]) { primes[cnt++] = i; euler[i] = i - 1; } for (int j = 0; primes[j] <= n / i; j++) { int t = primes[j] * i; st[t] = true; if (i % primes[j] == 0) { euler[t] = euler[i] * primes[j]; break; } euler[t] = euler[i] * (primes[j] - 1); } } }
快速幂
1 2 3 4 5 6 7 8 9 10 11 12
求 m^k mod p,时间复杂度 O(logk)。 intqmi(int m, int k, int p) { int res = 1 % p, t = m; while (k) { if (k&1) res = res * t % p; t = t * t % p; k >>= 1; } return res; }
扩展欧几里得算法
1 2 3 4 5 6 7 8 9 10 11
// 求x, y,使得ax + by = gcd(a, b) intexgcd(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; }
// a[N][N]是增广矩阵 intgauss(){ int c, r; for (c = 0, r = 0; c < n; c++) { int t = r; for (int i = r; i < n; i++) // 找到绝对值最大的行 if (fabs(a[i][c]) > fabs(a[t][c])) t = i;
if (fabs(a[t][c]) < eps) continue;
for (int i = c; i <= n; i++) swap(a[t][i], a[r][i]); // 将绝对值最大的行换到最顶端 for (int i = n; i >= c; i--) a[r][i] /= a[r][c]; // 将当前行的首位变成1 for (int i = r + 1; i < n; i++) // 用当前行将下面所有的列消成0 if (fabs(a[i][c]) > eps) 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]) > eps) return2; // 无解 return1; // 有无穷多组解 }
for (int i = n - 1; i >= 0; i--) for (int j = i + 1; j < n; j++) a[i][n] -= a[i][j] * a[j][n];
return0; // 有唯一解 }
递归法求组合数
1 2 3 4 5 6 7
// c[a][b] 表示从a个苹果中选b个的方案数 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;
// 首先预处理出所有阶乘取模的余数fact[N] ,以及所有阶乘取模的逆元infact[N] // 如果取模的数是质数,可以用费马小定理求逆元 intqmi(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; }
// 预处理阶乘的余数和阶乘逆元的余数 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; }
/* 若p是质数,则对于任意整数 1 <= m <= n,有: C(n, m) = C(n % p, m % p) * C(n / p, m / p)(mod p) */ intqmi(int a, int k, int p)// 快速幂模板 { int res = 1 % p; while (k) { if (k & 1) res = (LL)res * a % p; a = (LL)a * a % p; k >>= 1; } return res; }
intC(int a, int b, int p)// 通过定理求组合数C(a, b) { if (a < b) return0;
LL x = 1, y = 1; // x是分子,y是分母 for (int i = a, j = 1; j <= b; i--, j++) { x = (LL)x * i % p; y = (LL)y * j % p; }
return x * (LL)qmi(y, p - 2, p) % p; }
intlucas(LL a, LL b, int p){ if (a < p && b < p) returnC(a, b, p); return (LL)C(a % p, b % p, p) * lucas(a / p, b / p, p) % p; }