voidwrite(int x){ if (x < 0) putchar('-'), x = -x; if (x > 9) write(x / 10); putchar(x % 10 + '0'); }
qpow简洁版
1 2 3 4 5 6 7 8 9 10 11 12
using ll = longlong;
ll qpow(ll a, ll b, ll mod){ ll res = 1; a %= mod; while (b > 0) { if (b & 1) res = (res * a) % mod; a = (a * a) % mod; b >>= 1; } return res; }
快速乘
1 2 3 4 5 6
ll qmul(ll x, ll y, ll m){// 快速模乘:计算 (x * y) % m,避免大数乘法溢出 ll z = (longdouble) x / m * y + 0.5L;// 将 x / m * y 转为浮点数运算,估算商 z,z ≈ (x * y) / m u64 c = (unsignedlonglong) x * y - (unsignedlonglong) z * m;// 计算 c = (x * y) - z * m,得到模 m 后的结果 return c < m ? (ll) c : (ll) (c + m);// 如果 c < m,直接返回 c;否则返回 c + m,保证结果在 [0, m) 范围内 }
快速幂
1 2 3 4 5 6 7 8 9 10
// 快速幂:计算 (a^n) % m,高效处理大指数 ll qpow(ll a, ll n, ll m){ ll res = 1; // 初始化结果为 1 while (n) { // 当 n > 0 时循环 if (n & 1) res = qmul(res, a, m); // 如果 n 的最低位为 1,res = res * a % m a = qmul(a, a, m); // a = a * a % m n >>= 1; // n 右移一位 } return res; // 返回 (a^n) % m }
记住用法:
题目要求 (a^n) % m,直接调用 qpow(a, n, m)。
题目要求大数乘法 (x * y) % m,用 qmul(x, y, m)。
费马小定理求乘法逆元
mod必须是质数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
using ll = longlong;
// 依赖之前的 qpow 函数 ll qpow(ll a, ll b, ll mod){ ll res = 1; a %= mod; while (b > 0) { if (b & 1) res = (res * a) % mod; a = (a * a) % mod; b >>= 1; } return res; }
// 求 a 在 mod 下的逆元 ll inv(ll a, ll mod){ returnqpow(a, mod - 2, mod); }
除法取模
(a * qpow(b, p-2, p)) % p
1 2 3 4 5 6
// 假设要求 (A / B) % mod // 这里的 mod 必须是质数
ll inv_B = qpow(B, mod - 2, mod); // 算出 B 的逆元 ll ans = (A * inv_B) % mod; // 变成乘法运算
扩展欧几里得求逆元
mod 不是质数,但满足 gcd(a,mod)=1。
原理:求解 ax+by=1,则 x 即为逆元。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
using ll = longlong;
ll exgcd(ll a, ll b, ll &x, ll &y){ if (b == 0) { x = 1; y = 0; return a; } ll d = exgcd(b, a % b, y, x); y -= a / b * x; return d; }
ll inv_exgcd(ll a, ll mod){ ll x, y; ll d = exgcd(a, mod, x, y); if (d != 1) return-1; // 不存在逆元 return (x % mod + mod) % mod; // 调整为正数 }
// 2. 核心循环:只枚举到 sqrt(x) // 写法注意:用 i <= x / i 哪怕 x 接近 INT_MAX 也不会溢出 for (int i = 2; i <= x / i; i++) { if (x % i == 0) returnfalse; // 只要发现一个因子,就立即判定为合数 }
constint N = 1e7 + 10; // 10^7 级别 int primes[N], cnt; // primes存质数,cnt存质数个数 bool st[N]; // st[i]为true表示i是合数(被筛掉了),false表示是质数
voidget_primes(int n){ for (int i = 2; i <= n; i++) { // 如果没有被标记过,说明 i 是质数,加入 primes 列表 if (!st[i]) primes[cnt++] = i; // 核心循环:用当前的质数 primes[j] 去筛合数 for (int j = 0; primes[j] <= n / i; j++) { // primes[j] * i <= n // 1. 标记 primes[j] * i 为合数 st[primes[j] * i] = true; // 2. 【最关键的一行】 // 如果 i 能被 primes[j] 整除,说明 primes[j] 已经是 i 的最小质因子了 // 那么 primes[j] 也一定是 primes[j] * i 的最小质因子 // 我们必须立刻停止,避免重复筛 if (i % primes[j] == 0) break; } } }
intmain(){ int n = 100; get_primes(n);
for(int i = 0; i < cnt; i++) { cout << primes[i] << " "; } return0; }
高精度
输入输出
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
#include<iostream> #include<vector> #include<string> #include<algorithm>// max needs this usually usingnamespace std;
// 读入处理:string -> vector<int> (倒序) vector<int> s2v(string s){ vector<int> A; for (int i = s.size() - 1; i >= 0; i--) A.push_back(s[i] - '0'); return A; }
// 输出处理:倒序打印 voidprint(vector<int> &A){ for (int i = A.size() - 1; i >= 0; i--) cout << A[i]; cout << endl; }
比大小**(减法前判断大小)**
1 2 3 4 5 6 7 8
boolcmp(vector<int> &A, vector<int> &B){ if (A.size() != B.size()) return A.size() > B.size(); for (int i = A.size() - 1; i >= 0; i--) { if (A[i] != B[i]) return A[i] > B[i]; } returntrue; // A == B }
加法
1 2 3 4 5 6 7 8 9 10 11 12 13
vector<int> add(vector<int> &A, vector<int> &B){ vector<int> C; int t = 0; // 进位 for (int i = 0; i < A.size() || i < B.size(); i++) { if (i < A.size()) 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
vector<int> sub(vector<int> &A, vector<int> &B){ vector<int> C; int t = 0; // 借位 for (int i = 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; } // 去除前导0(即vector末尾的0),主要为了防止结果像 007 这种情况 while (C.size() > 1 && C.back() == 0) C.pop_back(); return C; }
// 快速幂计算 (base^exp) % mod ll qpow(ll base, ll exp){s ll res = 1; while (exp > 0) { if (exp & 1) res = res * base % MOD; base = base * base % MOD; exp >>= 1; } return res; }
// 初始化函数 - 时间复杂度 O(N) // 必须在主函数最开始调用一次 init() voidinit(){ fac[0] = 1; for (int i = 1; i < MAXN; i++) { fac[i] = fac[i - 1] * i % MOD; }
// 快速幂计算 (base^exp) % mod ll qpow(ll base, ll exp) { ll res = 1; while (exp > 0) { if (exp & 1) res = res * base % MOD; base = base * base % MOD; exp >>= 1; } return res; }
// 初始化函数 - 时间复杂度 O(N) // 必须在主函数最开始调用一次 init() voidinit() { fac[0] = 1; for (int i = 1; i < MAXN; i++) { fac[i] = fac[i - 1] * i % MOD; }
// Manacher算法求最长回文子串 intmanacher(string s){ // 1. 预处理字符串: "aba" -> "$#a#b#a#^" // $ 和 ^ 是哨兵字符,防止越界,# 是分隔符 string t = "$#"; for (char c : s) { t += c; t += '#'; } t += '^'; // 尾部哨兵
int n = t.size(); vector<int> p(n, 0); // p[i] 表示以 t[i] 为中心的回文半径 int mid = 0, r = 0; // mid 是当前右边界最远的回文串中心,r 是该回文串的右边界 int maxLen = 0;
for (int i = 1; i < n - 1; i++) { // 核心逻辑:利用对称性初始化 p[i] // i 关于 mid 的对称点是 2*mid - i if (i < r) { p[i] = min(p[2 * mid - i], r - i); } else { p[i] = 1; }
// “修改指令”变成“最终答案” voidbuild(int n){ // 变成二阶差分 for (int i = 1; i <= n + 3; i++) D3[i] += D3[i - 1]; // 变成一阶差分 for (int i = 1; i <= n + 3; i++) D3[i] += D3[i - 1]; // 变成原数组 for (int i = 1; i <= n + 3; i++) D3[i] += D3[i - 1]; }
// ================= 全局变量 ================= vector<int> adj[MAXN]; // 邻接表存图 int fa[MAXN][LOGN]; // 倍增数组:fa[u][i] 表示 u 的第 2^i 个祖先 int depth[MAXN]; // 深度数组 longlong diff[MAXN]; // 差分数组 (记得开 long long 防止爆) longlong ans[MAXN]; // 最终结果数组 int n, m;
// ================= 第一步:LCA 预处理 ================= // 功能:计算深度、初始化倍增数组 // 调用方式:dfs_lca(root, 0, 1); (root通常是1) voiddfs_lca(int u, int p, int d){ depth[u] = d; fa[u][0] = p; // 初始化倍增表 (2^1, 2^2 ... ) for (int i = 1; i < LOGN; i++) { fa[u][i] = fa[fa[u][i - 1]][i - 1]; } for (int v : adj[u]) { if (v != p) { dfs_lca(v, u, d + 1); } } }
// 功能:查询 u 和 v 的最近公共祖先 intget_lca(int u, int v){ if (depth[u] < depth[v]) swap(u, v); // 保证 u 更深 // 1. 把 u 跳到和 v 同一层 for (int i = LOGN - 1; i >= 0; i--) { if (depth[u] - (1 << i) >= depth[v]) { u = fa[u][i]; } } if (u == v) return u; // 如果跳到了同一个点,说明那个点就是 LCA // 2. u 和 v 一起向上跳,跳到 LCA 的下面一层 for (int i = LOGN - 1; i >= 0; i--) { if (fa[u][i] != fa[v][i]) { u = fa[u][i]; v = fa[v][i]; } } return fa[u][0]; // 再往上一步就是 LCA }
int n, k; cin >> n >> k; vector<int> a(n + 1); for (int i = 0; i < n; i++) { cin >> a[i]; } deque<int> dq; vi ans1(n + 1); for (int i = k - 1; i < n; i++) { while (!dq.empty() && dq.front() + k <= i) { dq.pop_front(); } while (!dq.empty() && a[dq.back()] > a[i]) {
dq.pop_back(); } dq.push_back(i); ans1[i] = a[dq.front()]; } for (int i = k - 1; i < n; i++) { cout << ans1[i] << " \n"[i == n - 1]; }
// 带权值的 Join:已知 x 和 y 的关系是 val (即 x 到 y 的距离/差值是 val) // 目标:把 x 的祖宗(rootX) 接到 y 的祖宗(rootY) 下面 voidjoin(int x, int y, int val){ int rootX = find(x); int rootY = find(y); if (rootX != rootY) { fa[rootX] = rootY; // 核心推导(向量运算): // d[rootX] + d[x] = d[y] + val // 所以 -> d[rootX] = d[y] + val - d[x] d[rootX] = d[y] + val - d[x]; } }
// 查 x 和 y 的关系 // 如果 isSame(x, y) 为真,那么 x 和 y 的相对距离就是 d[x] - d[y]
voiddfs(int x, int y){ st[x][y] = true; for (int i = 0; i < 4; i++) { int a = x + dx[i], b = y + dy[i]; // 越界、障碍、已访问检查 if (a < 0 || a >= n || b < 0 || b >= m) continue; if (g[a][b] != '0' && !st[a][b]) { // 假设 '0' 是水,非 '0' 是陆地 dfs(a, b); } } }
// 主循环 int cnt = 0; for (int i = 0; i < n; i++) for (int j = 0; j < m; j++) if (g[i][j] != '0' && !st[i][j]) { dfs(i, j); // 把这一个连通块全染成 true cnt++; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
//普通图 bool st[N]; vector<int> g[N];
voiddfs(int u){ st[u] = true; for (int v : g[u]) { if (!st[v]) dfs(v); } }
// 主循环 int cnt = 0; for (int i = 1; i <= n; i++) { if (!st[i]) { dfs(i); cnt++; } }
while (!q.empty()) { auto t = q.front(); q.pop(); if (t.x == end.x && t.y == end.y) return dist[t.x][t.y];
for (int i = 0; i < 4; i++) { int a = t.x + dx[i], b = t.y + dy[i]; // 越界 check、障碍物 check、是否已访问 check if (a >= 0 && a < n && b >= 0 && b < m && g[a][b] != '#' && dist[a][b] == -1) { dist[a][b] = dist[t.x][t.y] + 1; q.push({a, b}); } } } return-1; // 无法到达 }
constint N = 100005; // 根据题目要求设定 constint INF = 0x3f3f3f3f;
// 存图:vector<pair<邻居, 权值>> vector<pair<int, int>> adj[N]; int dist[N]; // 存储起点到各点的最短距离 bool in_queue[N]; // 标记是否在队列中 (避免重复入队) int cnt[N]; // 记录经过的边数,用于判断负环 int n, m;
// 返回 true 表示存在负环,false 表示正常求出了最短路 boolspfa(int s){ // 1. 初始化 memset(dist, 0x3f, sizeof(dist)); memset(in_queue, 0, sizeof(in_queue)); memset(cnt, 0, sizeof(cnt)); dist[s] = 0; queue<int> q; q.push(s); in_queue[s] = true; while (!q.empty()) { int u = q.front(); q.pop(); in_queue[u] = false; // 出队后标记为不在队列中 // 扫描所有出边 for (auto &edge : adj[u]) { int v = edge.first; int w = edge.second; // 松弛操作:如果通过 u 到 v 更近 if (dist[v] > dist[u] + w) { dist[v] = dist[u] + w; cnt[v] = cnt[u] + 1; // 记录路径长度 // 判断负环:如果一条路径经过了 >= n 个点,说明有环 if (cnt[v] >= n) returntrue; // 如果 v 不在队列中,则入队 // (这是 SPFA 比 Bellman-Ford 快的原因:只处理更新过的点) if (!in_queue[v]) { q.push(v); in_queue[v] = true; } } } } returnfalse; // 没有负环 }
// 查找 + 路径压缩 intfind(int x){ return x == fa[x] ? x : fa[x] = find(fa[x]); }
// 合并 x 和 y // 返回 true 表示合并成功(原来不连通) // 返回 false 表示原来就是连通的 boolmerge(int x, int y){ int rootX = find(x); int rootY = find(y); if (rootX == rootY) returnfalse;
// 启发式合并:把小的树接到大的树下面 if (sz[rootX] < sz[rootY]) std::swap(rootX, rootY); fa[rootY] = rootX; // Y 认 X 做爹 sz[rootX] += sz[rootY]; // X 吸收 Y 的人口 count--; // 连通块少一个 returntrue; }
// 判断是否连通 boolsame(int x, int y){ returnfind(x) == find(y); }
// 获取 x 所在连通块的大小 intsize(int x){ return sz[find(x)]; } };
template <typename T> structST { int n; vector<vector<T>> st;
// 构造函数:传入 vector<int> a 即可 // 默认实现的是区间【最大值】,如需最小值请把 max 改 min ST(const vector<T> &a) { n = a.size(); int K = __lg(n) + 1; // 计算需要的最大层数 st.assign(n, vector<T>(K));
// 1. 初始化第一层 (长度为 1) for (int i = 0; i < n; i++) st[i][0] = a[i];