在平时写算法的时候看到别人的代码,有的时候代码很短,但是一言难尽;也有的人代码很长,但是却可以看得下去,几乎不影响观感。那么这就引申出一个问题,代码怎么写才能看起来更加的“规范”,“美观”,代码模板怎么写才能够保证“泛用性”,“可读性”。
代码的格式问题
代码的基本格式我个人认为需要保证好基本的K&R规范或者BSD规范,之后再这两种规范下进行微调。
K&R规范的明显标志为 左大括号不换行 右大括号和该范围的起始语句列数上持平
BSD规范的明显标志为 左右大括号全部换行 都和该范围的起始语句列数上持平
具体的K&R规范和BSD规范可以参见如下文章c语言代码风格
其次看下面两段代码
#include <bits/stdc++.h>
using i64 = long long;using u64 = unsigned long long;using u32 = unsigned;using u128 = unsigned __int128;
constexpr int inf = 1E9;
void solve() { int n, m; std::cin >> n >> m;
std::vector<int> a(n), b(n), s(n); for (int i = 0; i < n; i++) { std::cin >> a[i] >> b[i] >> s[i]; }
auto ss = s; auto vs = a; vs.insert(vs.end(), b.begin(), b.end()); std::sort(ss.begin(), ss.end()); std::sort(vs.begin(), vs.end());
vs.erase(std::unique(vs.begin(), vs.end()), vs.end()); ss.erase(std::unique(ss.begin(), ss.end()), ss.end());
for (int i = 0; i < n; i++) { a[i] = std::lower_bound(vs.begin(), vs.end(), a[i]) - vs.begin(); b[i] = std::lower_bound(vs.begin(), vs.end(), b[i]) - vs.begin(); s[i] = std::lower_bound(ss.begin(), ss.end(), s[i]) - ss.begin(); }
std::vector<std::vector<int>> q(m); for (int i = 0; i < m; i++) { int k; std::cin >> k; q[i].resize(k); for (int j = 0; j < k; j++) { std::cin >> q[i][j]; q[i][j]--; } }
const int nv = vs.size(); const int ns = ss.size();
int maxa = -1;
std::vector<int> L(ns, -inf), R(ns, inf);
std::vector<std::vector<std::array<int, 2>>> ban(ns + 1); auto addSeg = [&](int s, int l, int r) { ban[s].push_back({l, r}); };
for (int i = m - 1; i >= 0; i--) { for (auto j : q[i]) { if (maxa < a[j]) { continue; } R[s[j]] = std::min(R[s[j]], b[j]); L[s[j]] = std::max(L[s[j]], maxa + 1); if (a[j] < maxa) { addSeg(ns, a[j] + 1, maxa); } } for (auto j : q[i]) { maxa = std::max(maxa, a[j]); } }
std::vector<std::vector<int>> vec(ns); for (int i = 0; i < n; i++) { vec[s[i]].push_back(i); }
for (int i = 0; i < ns; i++) { if (L[i] > R[i]) { std::cout << -1 << "\n"; return; } }
int mina = inf; for (int i = 0; i < m; i++) { for (auto j : q[i]) { if (mina > a[j]) { continue; } if (b[j] > a[j]) { addSeg(s[j], a[j] + 1, b[j]); } } for (auto j : q[i]) { mina = std::min(mina, a[j]); } }
std::vector<bool> can(nv); { std::vector<int> d(nv); for (auto [l, r] : ban[ns]) { d[l]++; if (r + 1 < nv) { d[r + 1]--; } } for (int i = 1; i < nv; i++) { d[i] += d[i - 1]; } for (int i = 0; i < nv; i++) { can[i] = (d[i] == 0); } }
std::vector<int> left(nv, -1); for (int i = 0; i < nv; i++) { if (i > 0) { left[i] = left[i - 1]; } if (can[i]) { left[i] = i; } }
std::vector<std::array<int, 2>> ans; for (int i = 0; i < ns; i++) { if (L[i] < 0) { continue; } int lst = -1; std::vector<std::array<int, 2>> e; for (auto [l, r] : ban[i]) { e.push_back({l - 1, -1}); e.push_back({r, 1}); } std::sort(e.begin(), e.end()); int sum = 0; int val = -1; for (auto [x, t] : e) { if (sum == 0 && x >= 0) { int u = left[std::min(x, R[i])]; if (u > lst && u >= L[i]) { val = u; } } sum += t; lst = x; } if (int u = left[R[i]]; u > lst && u >= L[i]) { val = u; } if (val == -1) { std::cout << -1 << "\n"; return; } ans.push_back({vs[val], ss[i]}); }
std::vector<int> score(n); std::sort(ans.begin(), ans.end()); std::map<int, int> diff; for (auto [d, p] : ans) { diff[p] = d; } for (int i = 0; i < n; i++) { score[i] += std::lower_bound(ans.begin(), ans.end(), std::array {vs[a[i]] + 1, 0}) - ans.begin(); if (diff.contains(ss[s[i]])) { int x = diff[ss[s[i]]]; if (vs[a[i]] < x && x <= vs[b[i]]) { score[i]++; } } }
int cur = 0; std::vector<std::set<int>> set(n + 1); for (int i = m - 1; i >= 0; i--) { for (auto j : q[i]) { int val = -1; for (int k = score[j]; k <= score[j] + 1 && k <= n; k++) { auto it = set[k].lower_bound(a[j]); if (it != set[k].begin()) { val = std::max(val, *std::prev(it)); } } if (val != -1) { int u = left[a[j]]; if (u <= val) { std::cout << -1 << "\n"; return; } for (int t = 0; t < 2; t++) { while (std::binary_search(ss.begin(), ss.end(), cur)) { cur++; } ans.push_back({vs[u], cur}); cur++; } } } for (auto j : q[i]) { set[score[j]].insert(a[j]); } }
std::cout << ans.size() << "\n"; for (auto [d, p] : ans) { std::cout << d << " " << p << "\n"; }
// { // std::vector<int> score(n); // for (int i = 0; i < n; i++) { // for (auto [d, p] : ans) { // if (vs[a[i]] >= d || (ss[s[i]] == p && vs[b[i]] >= d)) { // score[i]++; // } // } // } // for (int i = 0; i < m; i++) { // for (int j = i + 1; j < m; j++) { // for (auto x : q[i]) { // for (auto y : q[j]) { // assert(score[x] > score[y]); // } // } // } // } // assert(!ans.empty()); // for (int i = 0; i < ans.size(); i++) { // for (int j = 0; j < i; j++) { // assert(ans[i][1] != ans[j][1]); // } // } // }}
int main() { std::ios::sync_with_stdio(false); std::cin.tie(nullptr);
int t; std::cin >> t;
while (t--) { solve(); }
return 0;}
/*
|| ॐ नमः पार्वती पतये हर हर महादेव || ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣰⣷⣗⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡀⠀⠀⠀⠀⠀⠀⣠⣾⡿⠁⠈⢓⣟⡢⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣧⠀⠀⠀⠀⠀⣰⣿⠃⠀⠀⠀⠀⢐⣿⣼⠆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢿⣖⡀⠀⠀⠀⠈⠻⢗⣄⠀⢀⣴⣿⠟⠁⠀⠀⠀⠀⠀⢀⣴⡦⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⣿⡾⡢⣀⠀⠀⠀⠀⠛⠗⣿⠟⠁⠀⠀⠀⠀⢀⣠⣴⠿⠋⠻⣽⢆⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⣿⣝⢷⢦⣤⣠⣀⡀⠀⠁⠀⣀⣀⠀⣄⡶⠟⠋⠀⠀⡀⠀⠈⢻⣷⣄⠀⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⡀⠀⠐⠊⠉⠉⠉⠉⠛⠛⠷⠷⣴⡄⣀⠀⠈⠿⡧⡄⠉⠉⠁⠛⠛⠋⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣠⣾⡿⠋⠀⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⠀⠀⠀⢀⣤⣼⠿⠛⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠻⣿⢤⡀⠀⠛⢟⣄⠀⠀⠄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⣤⢿⡿⠃⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⠀⣤⣾⠟⠉⠀⠀⠀⠀⠠⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⢳⡻⡄⠀⠀⠐⠠⠥⣥⣀⣀⣄⣀⣀⣀⣠⣤⣴⠶⣚⠯⠚⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⠀⠈⢿⣧⠀⠀⠀⠀⠀⠀⠀⣀⣠⣤⣤⣦⣤⣄⡁⠠⠀⠀⠀⢳⣻⠀⠀⠀⠀⠀⠀⠈⠉⠉⠛⠛⠋⠉⠁⢀⣀⣀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⠀⠀⠀⢿⣷⠀⢀⣠⡴⣞⠿⠍⠁⠀⠀⠀⠀⠈⠙⠸⣷⣄⠀⠈⣽⡆⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⣴⡶⠈⠀⠉⠙⠛⠻⣷⣴⡀⡀⠀⠀⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⢻⣧⡿⠓⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⣾⡆⢨⣽⠃⠀⠀⠀⠀⠀⠀⠀⣠⣾⠟⠁⠀⠀⠂⠀⠀⢀⣀⣄⣉⠻⢾⣷⣄⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠋⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣨⡇⣾⡿⠀⠀⠀⠀⠀⠀⢀⣼⡿⠁⠀⠀⠀⠀⢠⣴⣿⠯⠋⠉⠙⠳⠰⣮⣻⣷⡀⠀⠀⠀ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⣾⠟⣼⣿⠁⠀⠀⠀⠀⠀⢠⣾⠟⠀⠀⠀⠀⠀⣴⣿⠟⠀⠀⠀⠀⠀⠀⠀⠈⢿⣿⡷⡀⠀⠀ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣦⡦⣤⣤⣤⣤⠤⠴⠰⠾⠛⢁⣾⡿⠁⠀⠀⠀⠀⠀⢠⣿⠋⠀⠀⠀⠀⠀⣼⡿⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⣧⡷⣳⠀⠀ ⠀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠠⠱⣷⡆⠀⠀⠀⠀⠀⢀⣠⢶⡿⠋⠀⠀⠀⠀⠀⠀⣔⡽⠃⠀⠀⠀⠀⠀⣼⡿⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣵⡍⣇⠀ ⢠⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢝⣷⡀⠀⠀⠀⠀⠸⣿⣫⣀⠀⠀⠀⠀⠀⢀⣼⠟⠀⠀⠀⠀⠀⢠⣞⡟⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⣿⢹⢾⠀ ⢸⠇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⣾⣇⠀⠀⠀⠀⠀⠀⠉⠙⠻⠷⠲⠖⠾⠛⠁⠀⠀⠀⣀⣠⣞⡷⠋⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠸⣿⢸⣾⡃ ⢸⣠⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠰⣯⠞⠛⠛⠛⠽⠶⣄⢀⡄⠀⠀⣴⢿⠻⠿⠿⠿⠛⠏⠃⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢂⣾⢸⣿⡅ ⢀⣏⡆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⠺⣷⣀⠙⣾⣆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⣿⠃⢸⣻⠆ ⠈⣿⢾⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠹⣿⡄⠹⡸⡄⠀⠀⠀⢸⠇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⣿⠏⠀⢾⣿⠀ ⠀⢻⡿⣱⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢹⡇⠀⣧⣷⠀⠀⠀⢸⣟⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣴⡿⠋⠀⠀⣇⡏⠀ ⠀⠈⣿⡼⡵⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣼⠇⠀⢸⢿⠀⠀⠀⣸⣯⢿⣵⣀⣀⠀⠀⠀⠀⠀⢀⢀⣤⣾⠟⠋⠀⠀⠀⣼⣻⠁⠀ ⠀⠀⢸⣯⡝⣮⢣⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣾⣿⠀⠀⢸⣿⠀⠀⠀⢸⣿⠂⠈⠙⠛⠛⠛⠛⠉⠉⠐⠒⠁⠀⠀⠀⠀⠀⢠⣷⡏⠀⠀ ⠀⠀⠀⢻⣷⡙⢷⢿⢤⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣾⡟⠃⠀⠀⣼⣿⠀⠀⠀⠀⢿⣷⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⢿⡞⠁⠀⠀ ⠀⠀⠀⠀⠻⣿⡄⠈⠛⠶⣵⣀⢀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⣤⠾⠛⠁⠀⠂⠀⢀⣷⡏⠀⠀⠀⠀⠈⢻⣷⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡰⣫⠟⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⠙⢿⣦⠀⠀⠈⠘⠙⠻⠟⠿⠷⠒⠚⠙⠁⠀⠈⠁⠀⠀⠀⠀⠀⢀⣾⡿⠀⠀⠀⠀⠀⠀⠀⠙⢿⡶⠠⢀⠀⠀⠀⠀⢀⣀⣄⠀⣠⠚⠁⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⠀⠈⠻⣿⣄⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⣿⡟⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠙⠒⠻⠿⠿⠛⠛⠉⠐⠁⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⠀⠀⠀⠈⠙⢙⠀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡀⣠⢞⡵⠋⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠃⠖⠶⣶⣤⣤⡤⢤⣤⡤⠶⠞⠫⠒⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
।। ऊँ कृष्णाय वासुदेवाय हरये परमात्मने ।। ।। प्रणतः क्लेशनाशाय गोविंदाय नमो नमः ।।
---------------> tiwari ji presents <---------------
“ Mnn boot karega k chor yrr apne se nahi hoga Just ask 1 question “ Why I started ? “
"Countless reasons not to do it, but only one reason to go for it — and that's enough."*/
#include <bits/stdc++.h>using namespace std;#define ll long long#define pb push_back#define pp pop_back()#define vi vector<ll>#define tiwari ll t ; cin>>t; while(t--)#define loop for(int i=0;i<n;i++){cin>>a[i];}#define mn ll m,n; cin>>m>>n;#define str string s ; cin>>s;#define str2 string s; cin>>s; string t; cin>>t;#define set unordered_set<int>seen;#define mii unordered_map<int,int>mp;#define mic unordered_map<int ,char>mpp;#define mcc unordered_map<char,char>mpc;#define srt sort(a.begin(),a.end());#define couta for(auto it:a){cout<<it<<" ";}#define bp1 __builtin_popcount#define bpll __builtin_popcountll#define countdigits int cg=(int)(log10(n)+1)#define surukezero __builtin_clz(a)#define lastkezero __builtin_ctz(a)#define decimalnumberdega auto number=0bn;#define ithbit1haiyanahi if((n>>i)&1){}#define last1removekaro n&(n-1)#define binaryme bitset<32>(xr).to_string();
int main() {
tiwari{
int n; cin>>n;
vi a(n); loop; srt;
int ans=0; for(int i=0;i<n-1;){ if(a[i]==a[i+1]){ ans=ans+1; i=i+2; } else{ i++; } } cout<<ans<<endl; }
return 0;}
/* JAB TAK KHUD KUCH NA KAR LO TABTAK DUSRO KO GYAN MAT DO */
上述两份代码皆选自Codeforces上的正确提交,谁的代码风格更好不言而喻。
第一份代码出自Jiangly。
第二份代码充斥了大量的宏定义,字符画,多余的空格,看似意义不明的函数,拥挤的代码…这份代码规范差,起手模板的宏定义可读性差,基本循环语句甚至包含了宏定义,码风极差。
关于代码的起手
常见的基础模版大体分为两种
- 基本代码模版
#include <bits/stdc++.h>using namespace std;void solve() {
}int main() { ios::sync_with_stdio(false); cin.tie(0), cout.tie(0); int tt = 1; // cin >> tt; while (tt--) { solve(); } return 0;}
- 长篇大论的包含宏定义+函数+重载等的模板,如
#include <bits/stdc++.h>// #include <bits/extc++.h>using namespace std;// using namespace __gnu_pbds;#define endl '\n'#define ture true#define flase false#define pow power#define all(x) begin(x), end(x)#define mem(a, x) memset(a, x, sizeof(a))#define gcd(a, b) gcdint(a, b)#define lcm(a, b) (a / gcd(a, b) * b)#define sz(x) (int)x.size()#define lowbit(x) (x & -x)#define pb push_back#define EPS 1e-7#define int ll#define ll long long#define i64 long long#define i128 __int128#define fr first#define sc second#define tcT template <class T#define tcTU tcT, class U
void unsyncIO() { cin.tie(0)->sync_with_stdio(0); }void setPrec() { cout << fixed << setprecision(15); }void setIO() { unsyncIO(), setPrec(); }
inline int gcdint(int a, int b) { return b ? gcdint(b, a % b) : a; }inline i128 gcd128(i128 a, i128 b) { return b ? gcd128(b, a % b) : a; }inline int cdiv(int a, int b) { return a / b + ((a ^ b) > 0 && a % b); }inline int fdiv(int a, int b) { return a / b - ((a ^ b) < 0 && a % b); }
tcT > using V = vector<T>;tcTU > using PR = pair<T, U>;tcTU > using MP = map<T, U>;tcTU > using VP = vector<pair<T, U>>;tcT > using pqg = priority_queue<T, vector<T>, greater<T>>;tcT > using pql = priority_queue<T, vector<T>, less<T>>;
tcTU > istream &operator>>(istream &in, pair<T, U> &a) { return in >> a.first >> a.second;}
tcT > istream &operator>>(istream &in, vector<T> &a) { for (auto &x : a) { in >> x; } return in;}
tcTU > ostream &operator<<(ostream &out, const pair<T, U> &a) { return out << a.first << ' ' << a.second;}
tcTU > ostream &operator<<(ostream &out, const vector<pair<T, U>> &a) { for (auto &x : a) { out << x << endl; } return out;}
tcT > ostream &operator<<(ostream &out, const vector<T> &a) { int n = a.size(); if (!n) { return out; } out << a[0]; for (int i = 1; i < n; i++) { out << ' ' << a[i]; } return out;}
std::ostream &operator<<(std::ostream &os, i128 n) { std::string s; while (n) { s += '0' + n % 10; n /= 10; } std::reverse(s.begin(), s.end()); return os << s;}
inline int power(int a, i64 b, int p = 1e9 + 7) { int res = 1; for (; b; b /= 2, a = 1LL * a * a % p) { if (b % 2) { res = 1LL * res * a % p; } } return res;}
tcT > bool ckmin(T &a, const T &b) { return b < a ? a = b, 1 : 0; }tcT > bool ckmax(T &a, const T &b) { return a < b ? a = b, 1 : 0; }
tcT > void remDup(vector<T> &v) { sort(all(v)); v.erase(unique(all(v)), end(v));}
tcTU > void erase(T &t, const U &u) { auto it = t.find(u); assert(it != end(t)); t.erase(it);}
tcTU > T fstTrue(T lo, T hi, U f) { hi++; assert(lo <= hi); while (lo < hi) { T mid = lo + (hi - lo) / 2; f(mid) ? hi = mid : lo = mid + 1; } return lo;}
tcTU > T lstTrue(T lo, T hi, U f) { lo--; assert(lo <= hi); while (lo < hi) { T mid = lo + (hi - lo + 1) / 2; f(mid) ? lo = mid : hi = mid - 1; } return lo;}
constexpr int mod = 1e9 + 7;constexpr int inf = 0x7fffffff;constexpr int N = 1.01e6;constexpr int M = 2.01e3;
#ifdef LOCAL#include <C:/Users/70510/Desktop/Others/algo/debug.h>#else#define debug(...) 42#endif
void solve() {}
signed main() { setIO(); int tt = 1; // cin >> tt; while (tt--) { solve(); } return 0;}
上面就是我的代码模板,我在此要强调的是,模板长≠模板冗长。
第一份代码简洁,我也推荐大家写这种代码,但是实际写下来肯定是不如第二份舒服。
第二份代码包含了大量的宏定义等行为,但是每一段定义做了区域性的划分
- 最初的#define保证了基本的可移植性 一直到自己的代码中如果有相关宏定义的话可能根本不报错
- 之后的函数是关于关闭流同步,基本短代码函数重载
- 之后的using使用template<class T>来定义部分STL容器,摒弃大量的vi,vii,pi,vpii等过长定义
- 重载部分STL的输入输出流以及int128定义,这里将重载运算符相关的代码放在了一起
- 接下来重载power函数,对基础pow进行直接加速
- 然后用template<class T>定义另一部分基本函数,取最大最小值赋值/二分/去重等函数
- 然后constexpr最常使用的四个int常量
- 引入本地debug.h
- 最后的主体代码
整体看下来,代码虽然很长,但基本逻辑是非常清晰的,对每一部分都进行了划分。现在的大多数橙名,红名以下选手的代码模板基本都是一通乱写,不知所云,这也是灰名和红名之间的差距,代码的可读性都是大大不同。
这里我的模板主要是从以下代码吸收而来,并借鉴了其良好的代码分区原则
#include <bits/stdc++.h>
using namespace std;
/////////////////////// MACROS ////////////////////////////////////////////using ll = long long;using ld = long double;using db = double;using str = string;
using pi = pair<int, int>;using pl = pair<ll, ll>;
using vi = vector<int>;using vl = vector<ll>;using vs = vector<str>;using vc = vector<char>;using vpi = vector<pi>;using vpl = vector<pl>;
#define tcT template <class T#define tcTU tcT, class UtcT > using V = vector<T>;tcT, size_t SZ > using AR = array<T, SZ>;tcTU > using PR = pair<T, U>;tcTU > using umap = unordered_map<T, U>;tcT > using uset = unordered_set<T>;tcT > using mset = multiset<T>;
#define mp make_pair#define f first#define s second
#define sz(x) int((x).size())#define all(x) x.begin(), x.end()#define rall(x) x.rbegin(), x.rend()#define rsz resize#define ins insert#define ft front()#define bk back()#define ppb pop_back()#define ppf pop_front()#define pb push_back#define eb emplace_back#define pf push_front
#define lb lower_bound#define ub upper_bound
// LOOPS#define FOR(i, a, b) for (int i = (a); i < (b); ++i)#define F0R(i, a) FOR(i, 0, a)#define ROF(i, a, b) for (int i = (b) - 1; i >= (a); --i)#define R0F(i, a) ROF(i, 0, a)#define rep(a) F0R(_, a)#define each(a, x) for (auto& a : x)
/////////////////////// IMPORANT VARS /////////////////////////////////////
const int MOD = 1e9 + 7; // 998244353;const int MX = 2e5 + 5;const ll INFL = ll(3e18) + 10;const int INF = int(1e9) + 10;const ld PI = acos((ld)-1);const int dx[4] = {1, 0, -1, 0}, dy[4] = {0, 1, 0, -1};tcT > using pqg = priority_queue<T, vector<T>, greater<T>>;tcT > using pql = priority_queue<T, vector<T>, less<T>>;mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());#define nl '\n'
constexpr int pct(int x) { return __builtin_popcount(x); } // # of bits setconstexpr int bits(int x) { return x == 0 ? 0 : 31 - __builtin_clz(x); }constexpr int p2(int x) { return 1 << x; }constexpr int msk2(int x) { return p2(x) - 1; }
ll cdiv(ll a, ll b) { return a / b + ((a ^ b) > 0 && a % b);} // divide a by b rounded upll fdiv(ll a, ll b) { return a / b - ((a ^ b) < 0 && a % b);} // divide a by b rounded down
tcT > bool ckmin(T& a, const T& b) { return b < a ? a = b, 1 : 0;} // set a = min(a,b)tcT > bool ckmax(T& a, const T& b) { return a < b ? a = b, 1 : 0; }
tcTU > T fstTrue(T lo, T hi, U f) { hi++; assert(lo <= hi); // assuming f is increasing while (lo < hi) { // find first index such that f is true T mid = lo + (hi - lo) / 2; f(mid) ? hi = mid : lo = mid + 1; } return lo;}tcTU > T lstTrue(T lo, T hi, U f) { lo--; assert(lo <= hi); // assuming f is decreasing while (lo < hi) { // find last index such that f is true T mid = lo + (hi - lo + 1) / 2; f(mid) ? lo = mid : hi = mid - 1; } return lo;}tcT > void remDup(vector<T>& v) { // sort and remove duplicates sort(all(v)); v.erase(unique(all(v)), end(v));}tcTU > void erase(T& t, const U& u) { // don't erase auto it = t.find(u); assert(it != end(t)); t.erase(it);} // element that doesn't exist from (multi)set
// #include <ext/pb_ds/assoc_container.hpp>// using namespace __gnu_pbds;
// tcT> using iset = tree<T, null_type, less<T>, rb_tree_tag,// tree_order_statistics_node_update>; #define ook order_of_key #define fbo// find_by_order
// struct chash {// const uint64_t C = ll(2e18*PI)+71;// const int RANDOM = rng();// ll operator()(ll x) const {// return __builtin_bswap64((x^RANDOM)*C); }// };
// struct splitmix64_hash {// static uint64_t splitmix64(uint64_t x) {// // http://xorshift.di.unimi.it/splitmix64.c// 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 =// std::chrono::steady_clock::now().time_since_epoch().count();// return splitmix64(x + FIXED_RANDOM);// }// };
// template<class K,class V> using um = unordered_map<K,V,chash>;// template<class K,class V> using ht = gp_hash_table<K,V,chash>;// template<class K,class V> V get(ht<K,V>& u, K x) {// auto it = u.find(x); return it == end(u) ? 0 : it->s; }
/////////////////////// OUPUT /////////////////////////////////////////////#define ts to_stringstr ts(char c) { return str(1, c); }str ts(const char* s) { return (str)s; }str ts(str s) { return s; }str ts(bool b) {#ifdef LOCAL return b ? "true" : "false";#else return ts((int)b);#endif}tcTU > str ts(pair<T, U> p) {#ifdef LOCAL return "(" + ts(p.f) + ", " + ts(p.s) + ")";#else return ts(p.f) + " " + ts(p.s);#endif}
tcTU > str ts(V<pair<T, U>> v) {#ifdef LOCAL bool fst = 1; str res = "{"; for (const auto& x : v) { if (!fst) res += ", "; fst = 0; res += ts(x); } res += "}"; return res;#else bool fst = 1; str res = ""; for (const auto& x : v) { if (!fst) res += " "; fst = 0; res += ts(x); } return res;#endif}
tcT > str ts(T v) {#ifdef LOCAL bool fst = 1; str res = "{"; for (const auto& x : v) { if (!fst) res += ", "; fst = 0; res += ts(x); } res += "}"; return res;#else bool fst = 1; str res = ""; for (const auto& x : v) { if (!fst) res += " "; fst = 0; res += ts(x); } return res;
#endif}
///////////////////////// DEBUG ///////////////////////////////////////////#define tcTUU tcT, class... Uvoid DBG() { cerr << "]" << "\e[0m" << endl; }tcTUU > void DBG(const T& t, const U&... u) { cerr << ts(t); if (sizeof...(u)) cerr << ", "; DBG(u...);}#ifdef LOCAL#define dbg(...) \ cerr << "\e[1m" << "Line(" << __LINE__ << ") -> [" << #__VA_ARGS__ \ << "]: [", \ DBG(__VA_ARGS__);#define asrt(...) \ if (!(__VA_ARGS__)) \ cerr << "Line(" << __LINE__ << ") -> function(" << __FUNCTION__ \ << ") -> CHK FAILED: (" << #__VA_ARGS__ << ")" << "\n", \ exit(0);#else#define dbg(...) 0#define asrt(...) 0#endif
///////////////////////// FILE I/O ////////////////////////////////////////void unsyncIO() { cin.tie(0)->sync_with_stdio(0); }void setPrec() { cout << fixed << setprecision(15); }void setIn(str s) { freopen(s.c_str(), "r", stdin); }void setOut(str s) { freopen(s.c_str(), "w", stdout); }void setIO(str s = "") { unsyncIO(); setPrec();#ifndef LOCAL if (sz(s)) setIn(s + ".in"), setOut(s + ".out"); // for USACO#endif}
///////////////////////// TEMPLATE ABOVE //////////////////////////////////
// REMEMBER// - Don't Focus On Only One Approach// - Read And Understand Problem Fully// - Think Of Edges Cases// - Implement Carefully// - Always Check For Overflows// - Reset Global Variables// - Look At The Bigger Picture// - Don't Get Discouraged, You Can Pull It Back
void solve() {}
int main() { setIO();
int TT = 1; // cin >> TT;
rep(TT) solve();
exit(0 - 0);}
这份代码由橙名所写,我个人认为,如果可读性是比较良好,并且非常值得借鉴的代码。
这里随机粘贴一位灰名选手的代码
//JAI SHREE RAM #include<bits/stdc++.h> #include<ext/pb_ds/assoc_container.hpp> #include<ext/pb_ds/tree_policy.hpp> using namespace std; using namespace std::chrono; #define ll long long using namespace __gnu_pbds; typedef tree<ll int,null_type,less_equal<ll int>,rb_tree_tag,tree_order_statistics_node_update> pbds;// find_by_order, order_of_key //for different comparator use greater instead of less void myerase(pbds &t, ll int v){ ll int rank = t.order_of_key(v);//Number of elements that are less than v in t pbds::iterator it = t.find_by_order(rank); //Iterator that points to the (rank+1)th element in t t.erase(it); } #define print_vector(v); for(int i=0;i<v.size();i++){cout<<v[i]<<" ";}cout<<endl; ll int MOD1=1000000007; ll int power_x_y(ll int x, ll int y)//logy time complexity//binary exponentiation { ll int temp; if (y == 0) return 1; temp = power_x_y(x, y / 2); if (y % 2 == 0) return (((temp)) * ((temp))); else return (((x)) *((temp)) * ((temp))); } ll int power_x_y_p(ll int x, ll int y,ll int p)//logy time complexity//binary exponentiation { ll int temp; if (y == 0) return 1; temp = power_x_y_p(x, y / 2,p)%p; if (y % 2 == 0) return (((temp%p)) * ((temp%p)))%p; else return ((((x%p) *(temp%p))%p) * (temp%p))%p; } //for 8 it will return 1000 vector<ll int> binary_representation(ll int n){ vector<ll int >v; while(n>0){ v.push_back(n%2); n/=2; } reverse(v.begin(),v.end()); return v; } void divisors(ll int n,vector<ll int>&v){ for(ll int i=1;i<=sqrt(n);i++){ if(n%i==0){ v.push_back(i); if(n/i!=n and n/i!=i){ v.push_back(n/i); } } } if(n!=1)v.push_back(n); sort(v.begin(),v.end()); } void generate_all_k_length_subsequences_of_s(ll int index,string s,vector<string>&storage,ll int k,string curr){ if(curr.size()==k){storage.push_back(curr);return;} if(index==s.size()) return ; //not pick generate_all_k_length_subsequences_of_s(index+1,s,storage,k,curr); //pick curr.push_back(s[index]); generate_all_k_length_subsequences_of_s(index+1,s,storage,k,curr); curr.pop_back(); return;
} void generate_all_subsets_of_s(ll int index,string s,vector<string>&storage,string curr){ if(index==s.size()){storage.push_back(curr);return;} //not pick generate_all_subsets_of_s(index+1,s,storage,curr); //pick curr.push_back(s[index]); generate_all_subsets_of_s(index+1,s,storage,curr); curr.pop_back(); return;
}
// Returns n^(-1) mod p unsigned long long modInverse(unsigned long long n, ll int p) { return power_x_y_p(n, p - 2, p); } unsigned long long nCrModPFermat( long long n,ll int r, ll int p) //time taken by all the functions involved=O(n + logp) { if (n < r) return 0; if (r == 0) return 1; //fest calculation of the three fact values in one go unsigned long long fac[n + 1]; fac[0] = 1; for (int i = 1; i <= n; i++) fac[i] = (fac[i - 1] * i) % p; return (((fac[n] * modInverse(fac[r], p)) % p)* (modInverse(fac[n - r], p) % p))% p; }
ll int getHighestSetBit(ll int n) { if (n == 0) return -1; int position = 0;
while (n > 1) { // Continue until n reduces to 1 n >>= 1; // Right shift the number by 1 position++; // Increment the position counter } return position; // Return the position of the highest set bit} bool isPrime(ll int n) { // Corner cases if (n <= 1) return false; if (n <= 3) return true;
// This is checked so that we can skip // middle five numbers in below loop if (n % 2 == 0 || n % 3 == 0) return false;
for (int i = 5; i * i <= n; i = i + 6) if (n % i == 0 || n % (i + 2) == 0) return false;
return true; } vector<ll int> Sieve_to_store_primes(ll int n) { bool prime[n + 1];memset(prime,1, sizeof(prime)); for (ll int p = 2; p<= n; p++) { if (prime[p] ==1) { for (ll int i=p*2; i <= n; i += p) prime[i] =0; } } vector<ll int>v; for (int p = 2; p <= n; p++){ if (prime[p]){ v.push_back(p); } } return v; } vector<ll int> Sieve_highest_lowest_primes(ll int n) { bool prime[n + 1];memset(prime,1, sizeof(prime)); vector<ll int>hp(n+1); vector<ll int>lp(n+1,0); for (ll int p = 2; p <= n; p++) { if (prime[p] ==1) { lp[p]=hp[p]=p;//if the number is a prime number then the lowest and the highest prime both are p for (ll int i = p * 2; i <= n; i += p){ prime[i] = false; hp[i]=p; if(lp[i]==0){ lp[i]=p; } } } } return lp;//return hp; //NOTE THAT THE HIGHEST PRIME AND THE LOWEST PRIME OF 0 AND 1 ARE MARKED AS 0 } vector<vector<ll int>> Sieve_all_divisors(ll int n){ vector<vector<ll int>>v(n+1); for(ll int i=1;i<=n;i++){ for(ll int j=i;j<=n;j+=i){ v[j].push_back(i); } } return v; } vector<vector<ll int>> Sieve_all_prime_factors(ll int n){ vector<ll int>lp=Sieve_highest_lowest_primes(n); vector<vector<ll int>>ans(n+1);//note that the prime factors of 0 and 1 are none for(ll int i=2;i<=n;i++){ ll int temp=i; while(temp>1){ ll int div=lp[temp]; ans[i].push_back(div); while(temp%div==0){ temp/=div; } } } return ans; }
ll int query_interactive(ll int x,ll int y){ cout<<"?"<<" "<<x<<" "<<y<<endl; ll int ans; cin>>ans; return ans; } void print_interactive(ll int x,ll int y){ cout<<"!"<<" "<<x<<" "<<y<<endl; } //__builtin_popcount(n) to count the number of set bits in a number//we can check if a number is a power of 2 using __builtin_popcount() //__builtin_clz will count the number of leading zeroes //reverse(v.begin(),v.end())//modifies the vector inplace //sort(v.rbegin(), v.rend());//to sort a vector in reverse order //sort(arr, arr + n, greater<ll int>());//sort an array in reverse order //fib(n)*fib(n+1)=sum of square of all fibs before n //there are approx n^1/3 divisors of a number //there are approx loglogn number of distinct prime factors of a number //there are approx n/logn different primes before a number n //gcd knowledge::: //gcd(a1,a2,a3,a4,a5..an)=gcd(a1,|a1-a2|,|a1-a3|,|a1-a4|,|a1-a5|....,|a1-an|) //gcd(a,b)=gcd(a,b%a)
//REMEMBER //all primes till a number A : A*loglog(A) //all highest primes till a number A: A*loglog(A)//all lowest primes till a number A: A*loglog(A) //all the divisors of all numbers till a max number A: A*log(A) //all the prime factors of N queries with max number A: A*loglog(A) + N*log(A)
int main(){ ios_base::sync_with_stdio(false); cin.tie(NULL);ll int t;t=1; ll int p=998244353; for(int o=0;o<t;o++){ ll int n;cin>>n; ll int a[n+1]={0}; for(int i=1;i<=n;i++){ cin>>a[i]; } vector<vector<ll int>>dist=Sieve_all_prime_factors(*max_element(a+1,a+n+1)); for(int i=1;i<=n;i++){ vector<ll int>v=dist[a[i]]; ll int mul=1; for(auto e:v){ mul*=e; } a[i]=mul; } vector<vector<ll int>>divisors=Sieve_all_divisors(*max_element(a+1,a+n+1));/* //testing for(int i=1;i<=n;i++){ cout<<a[i]<<" "; } cout<<endl; for (auto e:dist){ for(auto e2:e){ cout<<e2<<" "; } cout<<endl; } cout<<endl;cout<<endl; for (auto e:divisors){ for(auto e2:e){ cout<<e2<<" "; } cout<<endl; } cout<<"TESTING 1 OVER"<<endl; //testing over*/ ll int temp[(*max_element(a+1,a+n+1))+1];memset(temp,0,sizeof(temp)); ll int dp[n+1]={0}; for(int i=1;i<=n;i++){ //cout<<"i:"<<i<<endl; ll int cnt=dist[a[i]].size(); //cout<<"cnt:"<<cnt<<endl; ll int sum=0; ll int mx=((1<<cnt)-1); //cout<<"mx:"<<mx<<endl; for(ll int j=1;j<=mx;j++){ ll int t=j; ll int pos=0; ll int x=1;ll int set=0; while(t>0){ if(t%2==1){ x*=dist[a[i]][pos];set++; } t/=2; pos++;/* cout<<"pos:"<<pos<<endl; cout<<"t:"<<t<<endl; cout<<"x:"<<x<<endl;*/ } ll int sign=1;if(set%2==0)sign=(ll int)(-1); sum+=temp[x]*sign;sum%=p; } //cout<<"sum:"<<sum<<endl; if(i==1)sum=1; dp[i]=sum%p; for(int j=0;j<divisors[a[i]].size();j++){ temp[divisors[a[i]][j]]+=sum;temp[divisors[a[i]][j]]%=p; } } cout<<((dp[n]%p)+p)%p<<endl;
} return 0; }
这份代码模板完全不想看究竟定义了什么,重载了什么,而且代码之间没有空格,显得十分紧凑,阅读观感也极差。因此,一份良好的代码起手模板只有两种:
- 最基本的能保证运行的短代码模板
- 能够有着良好分区的长代码模板
算法模版
良好的算法模板必须由lambda/namespace/struct/template/代码片段封装,以此保证代码的可移植性,可读性。算法模板一定不能是一段针对于该算法的模板题目可以运行的代码程序。
- lambda
djikstra(); djikstra(s); 不写默认s=1
vector<int> dis(n + 1, 1E18);auto djikstra = [&](int s = 1) -> void { using PII = pair<int, int>; priority_queue<PII, vector<PII>, greater<PII>> q; q.emplace(0, s); dis[s] = 0; vector<int> vis(n + 1); while (!q.empty()) { int x = q.top().second; q.pop(); if (vis[x]) continue; vis[x] = 1; for (auto [y, w] : ver[x]) { if (dis[y] > dis[x] + w) { dis[y] = dis[x] + w; q.emplace(dis[y], y); } } }};
- namespace
eg:Geometry::Point<int> a, b;cin >> a >> b;cout << a + b << endl;
namespace Geometry { // 平面几何基础 using ld = long double; const ld PI = acos(-1); const ld EPS = 1e-7; const ld INF = numeric_limits<ld>::max(); #define cc(x) cout << fixed << setprecision(x);
ld fgcd(ld x, ld y) { // 实数域gcd return abs(y) < EPS ? abs(x) : fgcd(y, fmod(x, y)); } template<class T, class S> bool equal(T x, S y) { return -EPS < x - y && x - y < EPS; } template<class T> int sign(T x) { if (-EPS < x && x < EPS) return 0; return x < 0 ? -1 : 1; }
template<class T> struct Point { // 在C++17下使用 emplace_back 绑定可能会导致CE! T x, y; Point(T x_ = 0, T y_ = 0) : x(x_), y(y_) {} // 初始化 template<class U> operator Point<U>() { // 自动类型匹配 return Point<U>(U(x), U(y)); } Point &operator+=(Point p) & { return x += p.x, y += p.y, *this; } Point &operator+=(T t) & { return x += t, y += t, *this; } Point &operator-=(Point p) & { return x -= p.x, y -= p.y, *this; } Point &operator-=(T t) & { return x -= t, y -= t, *this; } Point &operator*=(T t) & { return x *= t, y *= t, *this; } Point &operator/=(T t) & { return x /= t, y /= t, *this; } Point operator-() const { return Point(-x, -y); } friend Point operator+(Point a, Point b) { return a += b; } friend Point operator+(Point a, T b) { return a += b; } friend Point operator-(Point a, Point b) { return a -= b; } friend Point operator-(Point a, T b) { return a -= b; } friend Point operator*(Point a, T b) { return a *= b; } friend Point operator*(T a, Point b) { return b *= a; } friend Point operator/(Point a, T b) { return a /= b; } friend bool operator<(Point a, Point b) { return equal(a.x, b.x) ? a.y < b.y - EPS : a.x < b.x - EPS; } friend bool operator>(Point a, Point b) { return b < a; } friend bool operator==(Point a, Point b) { return !(a < b) && !(b < a); } friend bool operator!=(Point a, Point b) { return a < b || b < a; } friend auto &operator>>(istream &is, Point &p) { return is >> p.x >> p.y; } friend auto &operator<<(ostream &os, Point p) { return os << "(" << p.x << ", " << p.y << ")"; } }; template<class T> struct Line { Point<T> a, b; Line(Point<T> a_ = Point<T>(), Point<T> b_ = Point<T>()) : a(a_), b(b_) {} template<class U> operator Line<U>() { // 自动类型匹配 return Line<U>(Point<U>(a), Point<U>(b)); } friend auto &operator<<(ostream &os, Line l) { return os << "<" << l.a << ", " << l.b << ">"; } };
template<class T> T cross(Point<T> a, Point<T> b) { // 叉乘 return a.x * b.y - a.y * b.x; } template<class T> T cross(Point<T> p1, Point<T> p2, Point<T> p0) { // 叉乘 (p1 - p0) x (p2 - p0); return cross(p1 - p0, p2 - p0); }
template<class T> T dot(Point<T> a, Point<T> b) { // 点乘 return a.x * b.x + a.y * b.y; } template<class T> T dot(Point<T> p1, Point<T> p2, Point<T> p0) { // 点乘 (p1 - p0) * (p2 - p0); return dot(p1 - p0, p2 - p0); }
template<class T> ld dis(T x1, T y1, T x2, T y2) { ld val = (x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2); return sqrt(val); } template<class T> ld dis(Point<T> a, Point<T> b) { return dis(a.x, a.y, b.x, b.y); }
template<class T> T dis1(Point<T> p1, Point<T> p2) { // 曼哈顿距离公式 return abs(p1.x - p2.x) + abs(p1.y - p2.y); }
Point<ld> standardize(Point<ld> vec) { // 转换为单位向量 return vec / sqrt(vec.x * vec.x + vec.y * vec.y); }
template<class T> Point<T> rotate(Point<T> p1, Point<T> p2) { // 旋转 Point<T> vec = p1 - p2; return {-vec.y, vec.x}; }} // namespace Geometry
- struct
const int base = 1000000000;const int base_digits = 9; // 分解为九个数位一个数字struct bigint { vector<int> a; int sign;
bigint() : sign(1) {} bigint operator-() const { bigint res = *this; res.sign = -sign; return res; } bigint(long long v) { *this = v; } bigint(const string &s) { read(s); } void operator=(const bigint &v) { sign = v.sign; a = v.a; } void operator=(long long v) { a.clear(); sign = 1; if (v < 0) sign = -1, v = -v; for (; v > 0; v = v / base) { a.push_back(v % base); } }
// 基础加减乘除 bigint operator+(const bigint &v) const { if (sign == v.sign) { bigint res = v; for (int i = 0, carry = 0; i < (int)max(a.size(), v.a.size()) || carry; ++i) { if (i == (int)res.a.size()) { res.a.push_back(0); } res.a[i] += carry + (i < (int)a.size() ? a[i] : 0); carry = res.a[i] >= base; if (carry) { res.a[i] -= base; } } return res; } return *this - (-v); } bigint operator-(const bigint &v) const { if (sign == v.sign) { if (abs() >= v.abs()) { bigint res = *this; for (int i = 0, carry = 0; i < (int)v.a.size() || carry; ++i) { res.a[i] -= carry + (i < (int)v.a.size() ? v.a[i] : 0); carry = res.a[i] < 0; if (carry) { res.a[i] += base; } } res.trim(); return res; } return -(v - *this); } return *this + (-v); } void operator*=(int v) { check(v); for (int i = 0, carry = 0; i < (int)a.size() || carry; ++i) { if (i == (int)a.size()) { a.push_back(0); } long long cur = a[i] * (long long)v + carry; carry = (int)(cur / base); a[i] = (int)(cur % base); } trim(); } void operator/=(int v) { check(v); for (int i = (int)a.size() - 1, rem = 0; i >= 0; --i) { long long cur = a[i] + rem * (long long)base; a[i] = (int)(cur / v); rem = (int)(cur % v); } trim(); } int operator%(int v) const { if (v < 0) { v = -v; } int m = 0; for (int i = a.size() - 1; i >= 0; --i) { m = (a[i] + m * (long long)base) % v; } return m * sign; }
void operator+=(const bigint &v) { *this = *this + v; } void operator-=(const bigint &v) { *this = *this - v; } bigint operator*(int v) const { bigint res = *this; res *= v; return res; } bigint operator/(int v) const { bigint res = *this; res /= v; return res; } void operator%=(const int &v) { *this = *this % v; }
bool operator<(const bigint &v) const { if (sign != v.sign) return sign < v.sign; if (a.size() != v.a.size()) return a.size() * sign < v.a.size() * v.sign; for (int i = a.size() - 1; i >= 0; i--) if (a[i] != v.a[i]) return a[i] * sign < v.a[i] * sign; return false; } bool operator>(const bigint &v) const { return v < *this; } bool operator<=(const bigint &v) const { return !(v < *this); } bool operator>=(const bigint &v) const { return !(*this < v); } bool operator==(const bigint &v) const { return !(*this < v) && !(v < *this); } bool operator!=(const bigint &v) const { return *this < v || v < *this; }
bigint abs() const { bigint res = *this; res.sign *= res.sign; return res; } void check(int v) { // 检查输入的是否为负数 if (v < 0) { sign = -sign; v = -v; } } void trim() { // 去除前导零 while (!a.empty() && !a.back()) a.pop_back(); if (a.empty()) sign = 1; } bool isZero() const { // 判断是否等于零 return a.empty() || (a.size() == 1 && !a[0]); } friend bigint gcd(const bigint &a, const bigint &b) { return b.isZero() ? a : gcd(b, a % b); } friend bigint lcm(const bigint &a, const bigint &b) { return a / gcd(a, b) * b; } void read(const string &s) { sign = 1; a.clear(); int pos = 0; while (pos < (int)s.size() && (s[pos] == '-' || s[pos] == '+')) { if (s[pos] == '-') sign = -sign; ++pos; } for (int i = s.size() - 1; i >= pos; i -= base_digits) { int x = 0; for (int j = max(pos, i - base_digits + 1); j <= i; j++) x = x * 10 + s[j] - '0'; a.push_back(x); } trim(); } friend istream &operator>>(istream &stream, bigint &v) { string s; stream >> s; v.read(s); return stream; } friend ostream &operator<<(ostream &stream, const bigint &v) { if (v.sign == -1) stream << '-'; stream << (v.a.empty() ? 0 : v.a.back()); for (int i = (int)v.a.size() - 2; i >= 0; --i) stream << setw(base_digits) << setfill('0') << v.a[i]; return stream; }
/* 大整数乘除大整数部分 */ typedef vector<long long> vll; bigint operator*(const bigint &v) const { // 大整数乘大整数 vector<int> a6 = convert_base(this->a, base_digits, 6); vector<int> b6 = convert_base(v.a, base_digits, 6); vll a(a6.begin(), a6.end()); vll b(b6.begin(), b6.end()); while (a.size() < b.size()) a.push_back(0); while (b.size() < a.size()) b.push_back(0); while (a.size() & (a.size() - 1)) a.push_back(0), b.push_back(0); vll c = karatsubaMultiply(a, b); bigint res; res.sign = sign * v.sign; for (int i = 0, carry = 0; i < (int)c.size(); i++) { long long cur = c[i] + carry; res.a.push_back((int)(cur % 1000000)); carry = (int)(cur / 1000000); } res.a = convert_base(res.a, 6, base_digits); res.trim(); return res; } friend pair<bigint, bigint> divmod(const bigint &a1, const bigint &b1) { // 大整数除大整数,同时返回答案与余数 int norm = base / (b1.a.back() + 1); bigint a = a1.abs() * norm; bigint b = b1.abs() * norm; bigint q, r; q.a.resize(a.a.size()); for (int i = a.a.size() - 1; i >= 0; i--) { r *= base; r += a.a[i]; int s1 = r.a.size() <= b.a.size() ? 0 : r.a[b.a.size()]; int s2 = r.a.size() <= b.a.size() - 1 ? 0 : r.a[b.a.size() - 1]; int d = ((long long)base * s1 + s2) / b.a.back(); r -= b * d; while (r < 0) r += b, --d; q.a[i] = d; } q.sign = a1.sign * b1.sign; r.sign = a1.sign; q.trim(); r.trim(); return make_pair(q, r / norm); } static vector<int> convert_base(const vector<int> &a, int old_digits, int new_digits) { vector<long long> p(max(old_digits, new_digits) + 1); p[0] = 1; for (int i = 1; i < (int)p.size(); i++) p[i] = p[i - 1] * 10; vector<int> res; long long cur = 0; int cur_digits = 0; for (int i = 0; i < (int)a.size(); i++) { cur += a[i] * p[cur_digits]; cur_digits += old_digits; while (cur_digits >= new_digits) { res.push_back((int)(cur % p[new_digits])); cur /= p[new_digits]; cur_digits -= new_digits; } } res.push_back((int)cur); while (!res.empty() && !res.back()) res.pop_back(); return res; } static vll karatsubaMultiply(const vll &a, const vll &b) { int n = a.size(); vll res(n + n); if (n <= 32) { for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { res[i + j] += a[i] * b[j]; } } return res; }
int k = n >> 1; vll a1(a.begin(), a.begin() + k); vll a2(a.begin() + k, a.end()); vll b1(b.begin(), b.begin() + k); vll b2(b.begin() + k, b.end());
vll a1b1 = karatsubaMultiply(a1, b1); vll a2b2 = karatsubaMultiply(a2, b2);
for (int i = 0; i < k; i++) a2[i] += a1[i]; for (int i = 0; i < k; i++) b2[i] += b1[i];
vll r = karatsubaMultiply(a2, b2); for (int i = 0; i < (int)a1b1.size(); i++) r[i] -= a1b1[i]; for (int i = 0; i < (int)a2b2.size(); i++) r[i] -= a2b2[i];
for (int i = 0; i < (int)r.size(); i++) res[i + k] += r[i]; for (int i = 0; i < (int)a1b1.size(); i++) res[i] += a1b1[i]; for (int i = 0; i < (int)a2b2.size(); i++) res[i + n] += a2b2[i]; return res; }
void operator*=(const bigint &v) { *this = *this * v; } bigint operator/(const bigint &v) const { return divmod(*this, v).first; } void operator/=(const bigint &v) { *this = *this / v; } bigint operator%(const bigint &v) const { return divmod(*this, v).second; } void operator%=(const bigint &v) { *this = *this % v; }};
- template
template<class T> struct Frac { T x, y; Frac() : Frac(0, 1) {} Frac(T x_) : Frac(x_, 1) {} Frac(T x_, T y_) : x(x_), y(y_) { if (y < 0) { y = -y; x = -x; } }
constexpr double val() const { return 1. * x / y; } constexpr Frac norm() const { // 调整符号、转化为最简形式 T p = gcd(x, y); return {x / p, y / p}; } friend constexpr auto &operator<<(ostream &o, const Frac &j) { T p = gcd(j.x, j.y); if (j.y == p) { return o << j.x / p; } else { return o << j.x / p << "/" << j.y / p; } } constexpr Frac &operator/=(const Frac &i) { x *= i.y; y *= i.x; if (y < 0) { x = -x; y = -y; } return *this; } constexpr Frac &operator+=(const Frac &i) { x = x * i.y + y * i.x; y *= i.y; return *this; } constexpr Frac &operator-=(const Frac &i) { x = x * i.y - y * i.x; y *= i.y; return *this; } constexpr Frac &operator*=(const Frac &i) { x *= i.x; y *= i.y; return *this; } friend constexpr Frac operator+(const Frac i, const Frac j) { return i += j; } friend constexpr Frac operator-(const Frac i, const Frac j) { return i -= j; } friend constexpr Frac operator*(const Frac i, const Frac j) { return i *= j; } friend constexpr Frac operator/(const Frac i, const Frac j) { return i /= j; } friend constexpr Frac operator-(const Frac i) { return Frac(-i.x, i.y); } friend constexpr bool operator<(const Frac i, const Frac j) { return i.x * j.y < i.y * j.x; } friend constexpr bool operator>(const Frac i, const Frac j) { return i.x * j.y > i.y * j.x; } friend constexpr bool operator==(const Frac i, const Frac j) { return i.x * j.y == i.y * j.x; } friend constexpr bool operator!=(const Frac i, const Frac j) { return i.x * j.y != i.y * j.x; }};
- 代码片段
int mul(int a, int b, int m) { int r = a * b - m * (int)(1.L / m * a * b); return r - m * (r >= m) + m * (r < 0);}int mypow(int a, int b, int m) { int res = 1 % m; for (; b; b >>= 1, a = mul(a, a, m)) { if (b & 1) { res = mul(res, a, m); } } return res;}
int B[] = {2, 3, 5, 7, 11, 13, 17, 19, 23};bool MR(int n) { if (n <= 1) return 0; for (int p : B) { if (n == p) return 1; if (n % p == 0) return 0; } int m = (n - 1) >> __builtin_ctz(n - 1); for (int p : B) { int t = m, a = mypow(p, m, n); while (t != n - 1 && a != 1 && a != n - 1) { a = mul(a, a, n); t *= 2; } if (a != n - 1 && t % 2 == 0) return 0; } return 1;}int PR(int n) { for (int p : B) { if (n % p == 0) return p; } auto f = [&](int x) -> int { x = mul(x, x, n) + 1; return x >= n ? x - n : x; }; int x = 0, y = 0, tot = 0, p = 1, q, g; for (int i = 0; (i & 255) || (g = gcd(p, n)) == 1; i++, x = f(x), y = f(f(y))) { if (x == y) { x = tot++; y = f(x); } q = mul(p, abs(x - y), n); if (q) p = q; } return g;}vector<int> fac(int n) { #define pb emplace_back if (n == 1) return {}; if (MR(n)) return {n}; int d = PR(n); auto v1 = fac(d), v2 = fac(n / d); auto i1 = v1.begin(), i2 = v2.begin(); vector<int> ans; while (i1 != v1.end() || i2 != v2.end()) { if (i1 == v1.end()) { ans.pb(*i2++); } else if (i2 == v2.end()) { ans.pb(*i1++); } else { if (*i1 < *i2) { ans.pb(*i1++); } else { ans.pb(*i2++); } } } return ans;}
上述五份代码,如果有良好的C++代码阅读基础,很容易看得懂,可移植性也非常高,分别为迪杰斯特拉,平面几何,高精度运算,分数四则运算,素数检测。
每一份代码都保证了该种算法的大体最优时间复杂度,而不是未经优化的基础算法模版。
综上,一份良好的算法模版应具有如下几点要求:
- 用lambda/namespace/struct/template/代码片段封装,大体为struct>template>namespace>lambda>代码片段封装,实际也需要根据算法的类型,长度,难易程度等来相应调整使用的方法。
- 具有优化后的大体最优时间复杂度
- 绝大多数情况是一段不可运行的代码,是算法模板,而不是可运行代码
- 保证可移植性,可读性等,例如Segt的模板不能只解决某一个特定区间问题,应该同时解决区间修改,区间询问等多种相关问题