(整理)常用模板

1|0 算法模板

1|1 快读 & 快写 

inline int read(){ int x = 0, f = 0; char ch = getchar(); while(!isdigit(ch)) f |= ch=='-', ch = getchar(); while(isdigit(ch)) x = x * 10 + (ch ^ 48), ch = getchar(); return f ? -x : x; } template <typename T> void print(T x) { if(x < 0) putchar('-'), x = -x; if(x > 9) print(x / 10); putchar(x % 10 + '0'); }

2|0 基础算法

2|1 高精度

高精度加法 // C = A + B, A >= 0, B >= 0 vector<int> add(vector<int> &A, vector<int> &B){ if (A.size() < B.size()) return add(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; } 高精度减法 // 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; } while (C.size() > 1 && C.back() == 0) C.pop_back(); return C; } 高精度乘低精度 // 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; } 高精度除以低精度 // 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; }

2|2 龟速乘 

int lpow(int x, int y){ int res = 0; while (y > 0) { if(y & 1) { res = (res + x) % mod; } y >>= 1; x = (x + x) % mod; } return res; }

2|3 二维前缀和

for(int i = 1; i <= N; i ++) for(int j = 1; j <= N; j ++) { c[i][j] = c[i - 1][j] + c[i][j - 1] - c[i - 1][j - 1] + c[i][j]; }

2|4 st表

O(nlog(n))预处理,O(1)查询,st(i, j) 数组表示下标 i 开始,长度为 2j 的区间(最大/最小值).

int st[N][22]; void init(int n){ for (int i = 1; i <= n; i ++) { st[i][0] = a[i]; } for (int j = 1; j <= 20; j ++) { for (int i = 1; i + (1 << j) - 1 <= n; i ++) { st[i][j] = std::max(st[i][j - 1], st[i + (1 << j - 1)][j - 1]); } } } int query (int l, int r) { if(l > r) std::swap(l, r); int k = std::log2(r - l + 1); return std::max(st[l][k], st[r - (1 << k) + 1][k]); }

2|5 二分 

返回第一个

while(l < r) { int mid = l + r >> 1; if (check(mid)) { r = mid; } else { l = mid + 1; } }

**返回最后一个 **

while(l < r) { int mid = l + r + 1 >> 1; if (check(mid)) { l = mid; } else { r = mid - 1; } }

通俗的二分

while(l <= r) { int mid = l + r >> 1; if(check(mid)){ ans = mid; l = mid + 1; } else { r = mid - 1; } }

浮点二分

double bsearch(double l, double r){ double eps = 1e-7; // eps 表示精度,取决于题目对精度的要求 while (r - l > eps){ double mid = (l + r) / 2; if (check(mid)) r = mid; else l = mid; } return l; }

1|0 求严格单调递增子序列长度 

for(int i = 1; i <= n; i ++){ if(a[i] > f[cnt]) f[++ cnt] = a[i]; else{ int l = 1, r = cnt; while(l < r){ int mid = l + r >> 1; if(a[i] <= f[mid]) r = mid; else l = mid + 1; } f[l] = a[i]; } }

3|0 DP

3|1 背包问题

背包问题的三种情况的处理方式

  1. 体积最多是V,能获得的最大价值 memset(f, 0, sizeof f)
  2. 体积恰好是V,能获得的最少价值 memset(f, 0x3f, sizeof f), f[0] = 0
  3. 体积至少是V 能获得的最少价值, memset(f, 0x3f, sizeof f), f[0] = 0, f[j] = min(f[max(0, j - v[i])], f[j])

3|2 最长公共子序列

for(int i = 1; i <= n; i ++) //第一个字符串 for(int j = 1; j <= n; j ++){ //第二个字符串 if(s1[i] == s2[j]) f[i][j] = f[i - 1][j - 1] + 1; else f[i][j] = max(f[i - 1][j], f[i][j - 1]); }

另外两个序列都是全排列的话,就可以找映射关系来求最长上升子序列。

3|3 树上求最长路径

// d1 和 d2 分别是节点向叶子节点的最长和次长距离 int dfs(int u, int fa){ int dist = 0; int d1 = 0, d2 = 0; for(int i = head[u]; i; i = node[i].ne){ int j = node[i].to, w = node[i].w; if(j == fa) continue; int d = dfs(j, u) + w; dist = max(dist, d); if(d >= d1) d2 = d1, d1 = d; else if(d >= d2) d2 = d; } ans = max(ans, d1 + d2); return dist; }

3|4 树形dp建虚树

int stk[N], dfn[N], top, col; vector<int> g[N]; /* LCA and dfn */ void in(int x){ if(top == 1) { stk[++ top] = x; return ; } int lca = LCA(stk[top], x); if(lca == stk[top]){ /* stk[++ top] = x; (需要该lca的儿子) */ return ; } while(top > 1 && dfn[stk[top - 1]] >= dfn[lca]){ g[stk[top - 1]].push_back(stk[top]); top --; } if(stk[top] != lca) { g[lca].push_back(stk[top]), stk[top] = lca; } stk[++ top] = x; } int main(){ ...... int k; cin >> k; for(int i = 1; i <= k; i ++) cin >> a[i]; sort(a + 1, a + k + 1, [&] (int x, int y){ return dfn[x] < dfn[y]; }); top = 0; stk[++ top] = 1; for(int i = 1; i <= k; i ++){ if(a[i] == 1) continue; in(a[i]); } while(top > 1) { g[stk[top - 1]].push_back(stk[top]); -- top; } ...... }

3|5 枚举子集

for (int i = x; i; i = (i - 1) & x)

4|0 数据结构

4|1 带size的并查集

struct DSU { std::vector<int> p, sz; DSU(int n) : p(n + 10), sz(n + 10, 1) { std::iota(p.begin(), p.end(), 0); } int find(int x) { return p[x] == x ? x : p[x] = find(p[x]); } void uni(int x, int y) { if (find(x) != find(y)) { sz[find(y)] += sz[find(x)]; } p[find(x)] = find(y); } bool same(int x, int y) { return find(x) == find(y); } int size(int x) { return sz[find(x)]; } };

4|2 并查集判二分图

struct DSU { std::vector<int> p; DSU(int n) : p(n + 10) { std::iota(p.begin(), p.end(), 0); } int find(int x) { return p[x] == x ? x : p[x] = find(p[x]); } void uni(int x, int y) { p[find(x)] = find(y); } bool same(int x, int y) { return find(x) == find(y); } }; struct Edge { int u, v; } edge[M]; bool check(int n, int m) { DSU dsu(n * 2); for (int i = 1; i <= m; i ++) { //合并所有边的两个端点 int u = edge[i].u, v = edge[i].v; dsu.uni(u, v + n), dsu.uni(u + n, v); } for (int i = 1; i <= n; i ++) {//判断是否有i与i+n在一个集合中 if (dsu.same(i, i + n)) { return false; } } return true; }

4|3 单调队列

1|0 滑动窗口

// 常见模型:找出滑动窗口中的最大值/最小值 int q[N], hh = 0, tt = -1; for (int i = 0; i < n; i ++ ) { while (hh <= tt && check_out(q[hh])) hh ++ ; // 判断队头是否滑出窗口 while (hh <= tt && check(q[tt], i)) tt -- ; q[++ tt] = i; }

4|4 KMP

// s[]是长文本,p[]是模式串,n是s的长度,m是p的长度 // 求模式串的Next数组: for (int i = 2, j = 0; i <= m; i ++){ while (j && p[i] != p[j + 1]) j = ne[j]; if (p[i] == p[j + 1]) j ++; ne[i] = j; } // 匹配 for (int i = 1, j = 0; i <= n; i ++){ while (j && s[i] != p[j + 1]) j = ne[j]; if (s[i] == p[j + 1]) j ++; if (j == m){ j = ne[j]; } }

4|5 线段树 (二)

#include <bits/stdc++.h> using i64 = long long; struct SegmentTree { const int n, p; struct Node{ int l, r; i64 sum, add, mul; }; std::vector<Node> tr; SegmentTree(int n, int p) : n(n), p(p), tr(4 << std::__lg(n)) {} SegmentTree(std::vector<int> init, int x) : SegmentTree(init.size() - 1, x) { std::function<void(int, int, int)> build = [&](int u, int l, int r) { tr[u].l = l, tr[u].r = r; tr[u].mul = 1; if(l == r) tr[u].sum = init[l]; else { int mid = l + r >> 1; build(u << 1, l, mid); build(u << 1 | 1, mid + 1, r); pushup(u); } }; build(1, 1, n); } void pushup(int u) { tr[u].sum = (tr[u << 1].sum + tr[u << 1 | 1].sum) % p; } void eval(Node &t, i64 mul, i64 add) { t.sum = (t.sum * mul % p + (t.r - t.l + 1) * add) % p; t.mul = t.mul * mul % p; t.add = (t.add * mul + add) % p; } void pushdown(int u) { eval(tr[u << 1], tr[u].mul, tr[u].add); eval(tr[u << 1 | 1], tr[u].mul, tr[u].add); tr[u].mul = 1, tr[u].add = 0; } void modify(int u, int l, int r, i64 add, i64 mul) { if(tr[u].l >= l && tr[u].r <= r){ eval(tr[u], mul, add); } else { pushdown(u); int mid = tr[u].l + tr[u].r >> 1; if(l <= mid) modify(u << 1, l, r, add, mul); if(r > mid) modify(u << 1 | 1, l, r, add, mul); pushup(u); } } i64 query(int u, int l, int r){ if(tr[u].r < l || tr[u].l > r) return 0; if(tr[u].l >= l && tr[u].r <= r) return tr[u].sum; pushdown(u); int mid = tr[u].l + tr[u].r >> 1; return (query(u << 1, l, r) + query(u << 1 | 1, l, r)) % p; } }; signed main() { std::ios::sync_with_stdio(false); std::cin.tie(nullptr); int n, m, p; std::cin >> n >> m >> p; std::vector<int> v(n + 1); for(int i = 1; i <= n; i ++) std::cin >> v[i]; SegmentTree tr(v, p); while(m --) { int op, l, r, d; std::cin >> op >> l >> r; if(op == 1) { std::cin >> d; tr.modify(1, l, r, 0, d); } else if(op == 2) { std::cin >> d; tr.modify(1, l, r, d, 1); } else std::cout << tr.query(1, l, r) << '\n'; } return 0; }

4|6 线段树合并

void merge (int &a, int b, int L, int R) { if (!a || !b) { a = (!b ? a : b); return ; } if (L == R) { /*区间更新*/ } else { int mid = L + R >> 1; merge (lson[a], lson[b], L, mid); merge (rson[a], rson[b], mid + 1, R); pushup (a); } }

4|7 splay

const int N = 100010; int n, m; struct Node { int s[2], p, v; int size, flag; void init(int _v, int _p) { v = _v, p = _p; size = 1; } }tr[N]; int root, idx; void pushup(int x) { tr[x].size = tr[tr[x].s[0]].size + tr[tr[x].s[1]].size + 1; } void pushdown(int x) { if (tr[x].flag) { swap(tr[x].s[0], tr[x].s[1]); tr[tr[x].s[0]].flag ^= 1; tr[tr[x].s[1]].flag ^= 1; tr[x].flag = 0; } } void rotate(int x) { int y = tr[x].p, z = tr[y].p; int k = tr[y].s[1] == x; // k=0表示x是y的左儿子;k=1表示x是y的右儿子 tr[z].s[tr[z].s[1] == y] = x, tr[x].p = z; tr[y].s[k] = tr[x].s[k ^ 1], tr[tr[x].s[k ^ 1]].p = y; tr[x].s[k ^ 1] = y, tr[y].p = x; pushup(y), pushup(x); } void splay(int x, int k) { while (tr[x].p != k) { int y = tr[x].p, z = tr[y].p; if (z != k) if ((tr[y].s[1] == x) ^ (tr[z].s[1] == y)) rotate(x); else rotate(y); rotate(x); } if (!k) root = x; } void insert(int v) { int u = root, p = 0; while (u) p = u, u = tr[u].s[v > tr[u].v]; u = ++ idx; if (p) tr[p].s[v > tr[p].v] = u; tr[u].init(v, p); splay(u, 0); //将插入节点旋转到根节点,保证时间复杂度 } int get_k(int k) { int u = root; while (true) { pushdown(u); if (tr[tr[u].s[0]].size >= k) u = tr[u].s[0]; else if (tr[tr[u].s[0]].size + 1 == k) return u; else k -= tr[tr[u].s[0]].size + 1, u = tr[u].s[1]; } return -1; } void output(int u) { pushdown(u); if (tr[u].s[0]) output(tr[u].s[0]); if (tr[u].v >= 1 && tr[u].v <= n) printf("%d ", tr[u].v); if (tr[u].s[1]) output(tr[u].s[1]); } int main() { scanf("%d%d", &n, &m); for (int i = 0; i <= n + 1; i ++ ) insert(i); while (m -- ) { int l, r; scanf("%d%d", &l, &r); l = get_k(l), r = get_k(r + 2); splay(l, 0), splay(r, l); tr[tr[r].s[0]].flag ^= 1; } output(root); return 0; }

4|8 树链剖分

int n, idx, w[N]; int dfn[N], nw[N], top[N]; int son[N], fa[N], dep[N], sz[N]; std::vector<int> G[N]; struct Tree{ int l, r; int add, sum; }tr[N << 2]; void dfs1(int u, int father, int depth){ dep[u] = depth, fa[u] = father, sz[u] = 1; for(auto v : G[u]){ if(v == father) continue; dfs1(v, u, depth + 1); sz[u] += sz[v]; if(sz[son[u]] < sz[v]) son[u] = v; } } void dfs2(int u, int t){ dfn[u] = ++ idx, nw[idx] = w[u], top[u] = t; if(!son[u]) return ; dfs2(son[u], t); for(auto v : G[u]){ if(v == fa[u] || v == son[u]) continue; dfs2(v, v); } } void pushup(int u){ tr[u].sum = tr[u << 1].sum + tr[u << 1 | 1].sum; } void pushdown(int u){ auto &root = tr[u], &left = tr[u << 1], &right = tr[u << 1 | 1]; if(root.add){ left.add += root.add, left.sum += (left.r - left.l + 1) * root.add; right.add += root.add, right.sum += (right.r - right.l + 1) * root.add; root.add = 0; } } void build(int u, int l, int r){ tr[u] = {l, r, 0, nw[r]}; if(l == r) return ; int mid = l + r >> 1; build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r); pushup(u); } void update(int u, int l, int r, int k){ if(l <= tr[u].l && r >= tr[u].r){ tr[u].add += k; tr[u].sum += k * (tr[u].r - tr[u].l + 1); return ; } pushdown(u); int mid = tr[u].r + tr[u].l >> 1; if(l <= mid) update(u << 1, l, r, k); if(r > mid) update(u << 1 | 1, l, r, k); pushup(u); } int query(int u, int l, int r){ if(l <= tr[u].l && r >= tr[u].r) return tr[u].sum; pushdown(u); int mid = tr[u].l + tr[u].r >> 1; int res = 0; if(l <= mid) res += query(u << 1, l, r); if(r > mid) res += query(u << 1 | 1, l, r); return res; } void update_path(int u, int v, int k){ while(top[u] != top[v]){ if(dep[top[u]] < dep[top[v]]) std::swap(u, v); update(1, dfn[top[u]], dfn[u], k); u = fa[top[u]]; } if(dep[u] < dep[v]) std::swap(u, v); update(1, dfn[v], dfn[u], k); } void update_tree(int u, int k){ update(1, dfn[u], dfn[u] + sz[u] - 1, k); } int query_path(int u, int v){ int res = 0; while(top[u] != top[v]){ if(dep[top[u]] < dep[top[v]]) std::swap(u, v); res += query(1, dfn[top[u]], dfn[u]); u = fa[top[u]]; } if(dep[u] < dep[v]) std::swap(u, v); res += query(1, dfn[v], dfn[u]); return res; } int query_tree(int u){ return query(1, dfn[u], dfn[u] + sz[u] - 1); }

4|9 树状数组

template <typename T> struct BIT { int n; std::vector<T> a; BIT(int n) : n(n), a(n + 1) {} void add(int u, T v) { for (int i = u; i <= n; i += i & -i) { a[i] += v; } } T sum(int u) { T ans = 0; for (int i = u; i > 0; i -= i & -i) { ans += a[i]; } return ans; } T rangeSum(int l, int r) { return sum(r) - sum(l - 1); } }; template <typename T> struct BIT { int n; std::vector<T> a; BIT(int n) : n(n), a(n + 10) {} void update(int u, T v) { for (int i = u; i <= n; i += i & -i) { a[i] = std::max(a[i], v); } } T query(int u) { T ans = 0; for (int i = u; i > 0; i -= i & -i) { ans = std::max(ans, a[i]); } return ans; } };

4|10 01字典树

int next[N << 4][2]; void insert (int x) { int cur = 0; for (int i = 31; i >= 0; i --) { int bit = x >> i & 1; if (!next[cur][bit]) { next[cur][bit] = ++ idx; } cur = next[cur][bit]; } num[cur] = x; } int query (int x) { int cur = 0; for (int i = 31; i >= 0; i --) { int bit = x >> i & 1; if (next[cur][bit ^ 1]) { cur = next[cur][bit ^ 1]; } else { cur = next[cur][bit]; } } return x ^ num[cur]; }

4|11 字符串哈希

核心思想:将字符串看成P进制数,P的进值是131或13331,取这两个值的冲突概率低(0.01%)
小技巧:取模的数用 264,这样直接用unsigned long long存储,溢出的结果就是取模的结果​

typedef unsigned long long ull; ull h[N], p[N], P = 131; // h[k]存储字符串前k个字母的哈希值, p[k]存储 P^k mod 2^64 // 初始化 p[0] = 1; for (int i = 1; i <= n; i ++){ h[i] = h[i - 1] * P + str[i]; p[i] = p[i - 1] * P; } // 计算子串 str[l ~ r] 的哈希值 ull get(int l, int r){ return h[r] - h[l - 1] * p[r - l + 1]; }

5|0 图论

5|1 LCA 求最近公共祖先

int depth[N], fa[N][25]; std::vector<int> G[N]; void dfs(int u, int father){ depth[u] = depth[father] + 1; fa[u][0] = father; for (int i = 1; i < 21; i ++) { fa[u][i] = fa[fa[u][i - 1]][i - 1]; } for (auto v : G[u]) { if (v == father) continue; dfs (v, u); } } int LCA(int a, int b){ if(depth[a] < depth[b]) std::swap(a, b); for(int k = 20; k >= 0; k --) if(depth[fa[a][k]] >= depth[b]) a = fa[a][k]; if(a == b) return a; for(int k = 20; k >= 0; k --) if(fa[a][k] != fa[b][k]) a = fa[a][k], b = fa[b][k]; return fa[a][0]; }

5|2 dijkstra

std::vector<int> dis(n + 1, -1); std::priority_queue<std::pair<int, int>, std::vector<std::pair<int, int>>, std::greater<>> q; q.push({0, 0}); while (!q.empty()) { auto [d, u] = q.top(); q.pop(); if (dis[u] != -1) continue; dis[u] = d; for (auto &[v, w] : G[u]) { q.push({d + w, v}); } }

6|0 数学

6|1 线性筛

const int N = ?; std::vector<int> is_primes(N + 1, 1); is_primes[0] = is_primes[1] = 0; std::vector<int> primes; for (int i = 2; i <= N; i++) { if (is_primes[i]) { primes.push_back(i); } for (auto p : primes) { if (1ll * i * p > N) break; is_primes[i * p] = 0; if (i % p == 0) break; } }

6|2 线性筛求莫比乌斯函数

const int N = ?; std::vector<int> is_primes(N + 1, 1), mu(N + 1); is_primes[0] = is_primes[1] = 0; std::vector<int> primes; mu[1] = 1; for (int i = 2; i <= N; i ++) { if (is_primes[i]) { primes.push_back(i); mu[i] = -1; } for (auto p : primes) { if (1ll * i * p > N) break; is_primes[i * p] = 0; if (i % p == 0) break; mu[p * i] = -mu[i]; } } std::vector<int> sum(N + 1); for (int i = 1; i <= N; i ++) { sum[i] = sum[i - 1] + mu[i]; }

6|3 欧拉函数

int phi (int a) { int res = a; for(int i = 2; i <= a / i; i ++) { if(a % i == 0) { res = res / i * (i - 1); while(a % i == 0) a /= i; } } if(a > 1) res = res / a * (a - 1); return res; }

筛法求欧拉函数

//int cnt = 0; std::vector<int> euler(n + 1), st(n + 1), primes(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] = 1; if(i % primes[j] == 0) { euler[t] = euler[i] * primes[j]; break; } euler[t] = euler[i] * (primes[j] - 1); } } //-------------------------------------------------- const int N = 1000010; std::vector<int> euler(N), is_primes(N, 1), primes; void init (int n) { euler[1] = 1; is_primes[0] = is_primes[1] = 0; for (int i = 2; i <= n; i ++) { if (is_primes[i]) { primes.push_back(i); euler[i] = i - 1; } for (auto p : primes) { if (1ll * i * p > n) break; is_primes[i * p] = 0; if (i % p == 0) { euler[i * p] = euler[i] * p; break ; } euler[i * p] = euler[i] * (p - 1); } } }

6|4 康托展开

#include <bits/stdc++.h> #define int long long const int N = 1e6 + 10, mod = 998244353; int num[N], fac[N]; void update (int x) { for (int i = x; i < N; i += i & -i) { num[i] ++; } } int query (int x) { int ans = 0; for (int i = x; i > 0; i -= i & -i) { ans += num[i]; } return ans; } signed main() { std::ios::sync_with_stdio(false); std::cin.tie(nullptr); fac[1] = 1; for (int i = 2; i < N; i ++) { fac[i] = fac[i - 1] * i % mod; } int n; std::cin >> n; std::vector<int> v(n + 1); for (int i = 1; i <= n; i ++) { std::cin >> v[i]; } int ans = 0; for (int i = 1; i <= n; i ++) { update (v[i]); int cnt = v[i] - query (v[i]); ans = ans + cnt * fac[n - i] % mod; ans %= mod; } std::cout << ans + 1 << '\n'; return 0; }

6|5 组合数

预处理逆元求组合数

int fact[N], infact[N]; int qmi (int a, int b, int p) { int res = 1 % p; a %= p; while (b > 0) { if(b & 1) res = res * a % p; a = a * a % p; b >>= 1; } return res; } void init (int n) { fact[0] = infact[0] = 1; for (int i = 1; i <= n; i ++) { fact[i] = fact[i - 1] * i % mod; infact[i] = qmi(fact[i], mod - 2, mod); } } int C (int a, int b) { //[ use init() ] and [long long] if (a < 0 || b < 0 || a < b) return 0; return fact[a] * infact[b] % mod * infact[a - b] % mod; } int P (int a, int b) { if(a < b) return 0; return fact[a] * infact[a - b] % mod; } //--------------------------------------- std::vector<Z> fac(n + 1), invfac(n + 1); fac[0] = 1; for (int i = 1; i <= n; i++) { fac[i] = fac[i - 1] * i; } invfac[n] = fac[n].inv(); for (int i = n; i; i--) { invfac[i - 1] = invfac[i] * i; } auto C = [&](int n, int m) -> Z { if (n < m || m < 0) { return 0; } return fac[n] * invfac[m] * invfac[n - m]; };

递推求组合数

int c[N][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; }

一个底和高分别为a,b的一个直角三角形,他的斜边经过的整数点的个数是 gcd(a, b) + 1

6|6 卡特兰数

给定n个0和n个1,它们按照某种顺序排成长度为2n的序列,满足任意前缀中0的个数都不少于1的个数的序列的数量为: Cat(n) = C(2n, n) / (n + 1)

6|7 1 ~ x 与 y互质的个数

vector<int> p; int f (int x, int y){ p.clear(); for (int i = 2; i * i <= y; i ++) { if (y % i == 0) { p.push_back(i); while(y % i == 0) { y /= i; } } } if (y > 1) { p.push_back(y); } int ans = 0, cnt = p.size(); for (int i = 1; i < (1 << cnt); i ++) { int tmp = 1, t = 0; for (int j = 0; j < cnt; j ++) { if (i >> j & 1) { tmp *= p[j]; t ++; } } ans += (t & 1 ? x / tmp : -x / tmp); } return x - ans; }

6|8 扩展欧几里德求逆元

int exgcd (int a, int b, int &x, int &y) { // 返回的是gcd (a, b) if (!b) { x = 1; y = 0; return a; } int d = exgcd(b, a % b, y, x); y -= (a / b) * x; return d; } int inv (int a, int b) { int x = 0, y = 0; exgcd(a, b, x, y); return (x % b + b) % b; }

6|9 中国剩余定理

6|10 FFT

#include <bits/stdc++.h> const int N = 3e5 + 10; const double PI = acos(-1); int n, m; struct Complex { double x, y; Complex operator+ (const Complex& t) const { return {x + t.x, y + t.y}; } Complex operator- (const Complex& t) const { return {x - t.x, y - t.y}; } Complex operator* (const Complex& t) const { return {x * t.x - y * t.y, x * t.y + y * t.x}; } }a[N], b[N]; int rev[N], bit, tot; void FFT (Complex a[], int inv) { for (int i = 0; i < tot; i ++) { if (i < rev[i]) { std::swap (a[i], a[rev[i]]); } } for (int mid = 1; mid < tot; mid <<= 1) { auto w1 = Complex({cos(PI / mid), inv * sin(PI / mid)}); for (int i = 0; i < tot; i += mid * 2) { auto wk = Complex({1, 0}); for (int j = 0; j < mid; j ++, wk = wk * w1) { auto x = a[i + j], y = wk * a[i + j + mid]; a[i + j] = x + y, a[i + j + mid] = x - y; } } } } int main() { int n, m; std::cin >> n >> m; for (int i = 0; i <= n; i ++) { std::cin >> a[i].x; } for (int i = 0; i <= m; i ++) { std::cin >> b[i].x; } while ((1 << bit) < n + m + 1) { bit ++; } tot = 1 << bit; for (int i = 0; i < tot; i ++) { rev[i] = (rev[i >> 1] >> 1) | (i & 1) << (bit - 1); } FFT(a, 1), FFT(b, 1); for (int i = 0; i < tot; i ++) { a[i] = a[i] * b[i]; } FFT(a, -1); for (int i = 0; i <= n + m; i ++) { std::cout << int(a[i].x / tot + 0.5) << ' '; } return 0; }

6|11 狄利克雷卷积

定义两个函数f, g 的狄利克雷卷积为 fg , 其自成一个函数。

有:(fg)(n)=d|nf(d)g(nd)

6|12 杜教筛

g(1)S(n)=i=1n(fg)(i)i=2ng(i)S(ni)

给定一个正整数,求

ans1=i=1nφ(i)

ans2=i=1nμ(i)

#include <bits/stdc++.h> #define int long long const int N = 2e6 + 10; int mu[N], sum_mu[N]; std::vector<int> primes, is_primes(N, 1); std::map<int, int> Mu; int sMu (int n) { if (n < N) return sum_mu[n]; if (Mu.count (n)) return Mu[n]; int res = 1; for (int l = 2, r; l <= n; l = r + 1) { r = n / (n / l); res -= (r - l + 1) * sMu (n / l); } return Mu[n] = res; } int sPhi (int n) { int res = 0; for (int l = 1, r; l <= n; l = r + 1) { r = n / (n / l); res += (sMu(r) - sMu(l - 1)) * (n / l) * (n / l); } return (res - 1) / 2 + 1; } void init (int n) { mu[1] = 1; is_primes[0] = is_primes[1] = 0; for (int i = 2; i <= n; i ++) { if (is_primes[i]) { primes.push_back(i); mu[i] = -1; } for (auto p : primes) { if (1ll * i * p > n) break; is_primes[i * p] = 0; if (i % p == 0) break ; mu[i * p] = -mu[i]; } } for (int i = 1; i <= n; i ++) { sum_mu[i] = sum_mu[i - 1] + mu[i]; } } signed main() { std::ios::sync_with_stdio(false); std::cin.tie(nullptr); init (2000000); int _; std::cin >> _; while (_ --) { int n; std::cin >> n; std::cout << sPhi (n) << ' ' << sMu (n) << '\n'; } return 0; }

6|13 判凸包

int n, stk[N], top; bool st[N]; struct Point { double x, y; bool operator < (const Point &t) const { if(x != t.x) return x < t.x; return y < t.y; } Point operator - (const Point &t) const { return (Point){x - t.x, y - t.y}; } }; Point q[N]; double get_dist (Point a, Point b) { double dx = a.x - b.x; double dy = a.y - b.y; return sqrt(dx * dx + dy * dy); } double cross (Point a, Point b) { return a.x * b.y - a.y * b.x; } double area (Point a, Point b, Point c) { return cross(b - a, c - a); } bool andrew() { std::sort(q, q + n); top = 0; for (int i = 0 ; i < n; i ++) { while(top >= 2 && area(q[stk[top - 1]], q[stk[top]], q[i]) <= 0) { if(area(q[stk[top - 1]], q[stk[top]], q[i]) < 0) { st[stk[top -- ]] = false; } else { top --; } } stk[++ top] = i; st[i] = true; } st[0] = false; for (int i = n - 1; i >= 0; i --) { if (st[i]) continue; while (top >= 2 && area(q[stk[top - 1]], q[stk[top]], q[i]) <= 0) { top --; } stk[++ top] = i; } return top == 5; }

7|0 小知识 

7|1 cf的保护分

前六场初始分:Promotions of the displayed rating will be equal to 500,350,250,150,100,50 (in total exactly 1400).

7|2 数据范围所能使用的时间复杂度 

7|3 cf 防止哈希被卡

struct custom_hash { static uint64_t splitmix64(uint64_t x) { x += 0x9e3779b97f4a7c15; x = (x ^ (x >> 30)) * 0xbf58476d1ce4e5b9; x = (x ^ (x >> 27)) * 0x94d049bb133111eb; return x ^ (x >> 31); } size_t operator()(uint64_t x) const { static const uint64_t FIXED_RANDOM = chrono::steady_clock::now().time_since_epoch().count(); return splitmix64(x + FIXED_RANDOM); } }; unordered_map<int, int, custom_hash> safe_map; //参考链接https://codeforces.com/blog/entry/62393

7|4 能用PII作哈希的键值

struct hashfunc{ template<typename T, typename U> size_t operator() (const pair<T, U> &i) const{ return hash<T>()(i.first) ^ hash<U>()(i.second); } };

7|5 Debug

std::string to_string(std::string s) { return '"' + s + '"'; } std::string to_string(char s) { return std::string(1, s); } std::string to_string(const char* s) { return to_string((std::string)s); } std::string to_string(bool b) { return (b ? "true" : "false"); } template <typename A> std::string to_string(A b) { return std::to_string(b); } template <typename A, typename B> std::string to_string(std::pair<A, B> p) { return "(" + to_string(p.first) + ", " + to_string(p.second) + ")"; } template <typename A> std::string to_string(std::vector<A> v) { bool f = 1; std::string r = "{"; for (auto& x : v) { if (!f) r += ", "; f = 0; r += to_string(x); } return r + "}"; } template <typename A, typename B> std::string to_string(std::map<A, B> map) { bool f = 1; std::string r = "{"; for (auto& [x, y] : map) { if (!f) r += ", "; f = 0; r += to_string(x) + ": " + to_string(y); } return r + "}"; } template <typename A> std::string to_string(std::multiset<A> set) { bool f = 1; std::string r = "{"; for (auto& x : set) { if (!f) r += ", "; f = 0; r += to_string(x); } return r + "}"; } template <typename A> std::string to_string(std::set<A> set) { bool f = 1; std::string r = "{"; for (auto& x : set) { if (!f) r += ", "; f = 0; r += to_string(x); } return r + "}"; } void debug_out() { std::cout << '\n'; } template <typename Head, typename... Tail> void debug_out(Head H, Tail... T) { std::cout << " " << to_string(H); debug_out(T...); } #define pr(...) std::cout << "[" << #__VA_ARGS__ << "] :", debug_out(__VA_ARGS__) #define dearr(arr, a, b) \ std::cout << #arr << " : "; \ for (int i = a; i <= b; i ++) \ std::cout << arr[i] << " "; \ std::cout << '\n'; #define demat(mat, row, col) \ std::cout << #mat << " :\n"; \ for (int i = 1; i <= row; i++) { \ std::cout << i << " : \n"; \ for (int j = 1; j <= col; j++) \ std::cout << mat[i][j] << " "; \ std::cout << '\n'; \ }

7|6 取模int

using i64 = long long; template<class T> constexpr T power(T a, i64 b) { T res = 1; for (; b; b /= 2, a *= a) { if (b % 2) { res *= a; } } return res; } template <int P> struct MInt { int x; constexpr MInt() : x{} {} constexpr MInt(i64 x) : x{norm(x % getMod())} {} static int Mod; constexpr static int getMod() { if (P > 0) { return P; } else { return Mod; } } constexpr static void setMod(int Mod_) { Mod = Mod_; } constexpr int norm(int x) const { if (x < 0) { x += getMod(); } if (x >= getMod()) { x -= getMod(); } return x; } constexpr int val() const { return x; } explicit constexpr operator int() const { return x; } constexpr MInt operator-() const { MInt res; res.x = norm(getMod() - x); return res; } constexpr MInt inv() const { assert(x != 0); return power(*this, getMod() - 2); } constexpr MInt &operator*=(MInt rhs) & { x = 1LL * x * rhs.x % getMod(); return *this; } constexpr MInt &operator+=(MInt rhs) & { x = norm(x + rhs.x); return *this; } constexpr MInt &operator-=(MInt rhs) & { x = norm(x - rhs.x); return *this; } constexpr MInt &operator/=(MInt rhs) & { return *this *= rhs.inv(); } friend constexpr MInt operator*(MInt lhs, MInt rhs) { MInt res = lhs; res *= rhs; return res; } friend constexpr MInt operator+(MInt lhs, MInt rhs) { MInt res = lhs; res += rhs; return res; } friend constexpr MInt operator-(MInt lhs, MInt rhs) { MInt res = lhs; res -= rhs; return res; } friend constexpr MInt operator/(MInt lhs, MInt rhs) { MInt res = lhs; res /= rhs; return res; } friend constexpr std::istream &operator>>(std::istream &is, MInt &a) { i64 v; is >> v; a = MInt(v); return is; } friend constexpr std::ostream &operator<<(std::ostream &os, const MInt &a) { return os << a.val(); } friend constexpr bool operator==(MInt lhs, MInt rhs) { return lhs.val() == rhs.val(); } friend constexpr bool operator!=(MInt lhs, MInt rhs) { return lhs.val() != rhs.val(); } }; using Z = MInt<998244353>;

7|7 臭氧优化 

#pragma GCC optimize(3,"Ofast","inline")

7|8 C++ STL简介

priority_queue, 优先队列,默认是大顶堆 size() empty() push() 插入一个元素 top() 返回堆顶元素 pop() 弹出堆顶元素 定义成小顶堆的方式:priority_queue<int, vector<int>, greater<int>> q; stack, 栈 size() empty() push() 向栈顶插入一个元素 top() 返回栈顶元素 pop() 弹出栈顶元素 deque, 双端队列 size() empty() clear() front()/back() push_back()/pop_back() push_front()/pop_front() begin()/end() [] bitset, bitset<10000> s; ~, &, |, ^ >>, << ==, != [] count() 返回有多少个1 any() 判断是否至少有一个1 none() 判断是否全为0 set() 把所有位置成1 set(k, v) 将第k位变成v reset() 把所有位变成0 flip() 等价于~ flip(k) 把第k位取反

__EOF__

本文作者ReSakura
本文链接https://www.cnblogs.com/ReSakura/p/16393342.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   ReSakura  阅读(41)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 记一次.NET内存居高不下排查解决与启示
点击右上角即可分享
微信分享提示