各种OI板子

以下内容不定时更新,想到啥写啥。。

读写优化

快读

code
template <class T> inline void read(T &res) { char ch = getchar(); bool f = 0; res = 0; for(; !isdigit(ch); ch = getchar()) f |= ch == '-'; for(; isdigit(ch); ch = getchar()) res = (res << 1) + (res << 3) + (ch ^ 48); res = f ? ~res + 1 : res; }

快写

code
template <class T> inline void waw(T x) { if(x > 9) waw(x / 10); putchar(x % 10 ^ 48); } template <class T> inline void ww(T x) { if(x < 0) x = ~x + 1, putchar('-'); waw(x); }

基础算法

排序

快排

直接用自带的函数实现即可,sort(起点,终点,排列规则(这个可以没有))。

归并

通常会用来求逆序对。

code
void merge_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]; }

O(n) 的基数排序(位运算版本)

最快的排序方法。

code
inline void Sort() { static ll p = 65535; for (rl i = 1; i <= n; ++i) c[a[i] & p]++; for (rl i = 1; i <= p; ++i) c[i] += c[i - 1]; for (rl i = n; i >= 1; --i) b[c[a[i] & p]--] = i; memset(c, 0, sizeof c); for (rl i = 1; i <= n; ++i) c[(a[b[i]] >> 16) & p]++; for (rl i = 1; i <= p; ++i) c[i] += c[i - 1]; for (rl i = n; i >= 1; --i) d[c[(a[b[i]] >> 16) & p]--] = b[i]; for (rl i = 1; i <= n; ++i) b[i] = d[i]; }

排完序后 b 中是排好的序列。


图论

最短路

Floyd

code
for(rl k=1; k <= n; ++ k) for(rl i=1; i <= n; ++ i) for(rl j=1; j <= n; ++ j) g[i][j] = min(g[i][j], g[i][k] + g[k][j]); // 传递闭包 #define for(i, a, b) for(rl i=a; i <= b; ++ i) for(k, 1, n) for(i, 1, n) for(j, 1, n) g[i][j] |= g[i][k] & g[k][j];

spfa

code
inline bool spfa() { queue<ll> q; memset(dis, 0x3f, sizeof dis); dis[s] = 0, q.push(s); while(!q.empty()) { ll u = q.front(); q.pop(); st[u] = false; for(rl i=h[u]; ~i; i = ne[i]) { ll v = e[i]; if(dis[v] > dis[u] + w[i]) { dis[v] = dis[u] + w[i]; sla[v] = sla[u] + 1; if(sla[v] >= n + 2) return true; // 判断是否存在负环 if(!st[v]) st[v] = true, q.push(v); } } } return true; }

dijkstra( + heap)

code
struct node { ll id, dis; bool operator <(const node &x) const { return dis > x.dis; } }; inline void dijkstra() { priority_queue<node> q; memset(dis, 0x3f, sizeof dis); dis[s] = 0, q.push({s, 0}); while(q.size()) { auto t = q.top(); q.pop(); ll u = t.id; if(st[u]) continue; st[u] = true; for(rl i=h[u]; ~i; i = ne[i]) { ll v = e[i]; if(dis[v] > dis[u] + w[i]) { dis[v] = dis[u] + w[i]; q.push({v, dis[v]}); } } } }

最小生成树

kruskal

code
struct node { ll u, v, w; bool used; }e[M]; bool cmp(node a, node b) { return a.w < b.w; } ll find(ll x) { return p[x] == x ? x : p[x] = find(p[x]); } int main() { scanf("%lld %lld", &n, &m); for(i ,1, n) p[i] = i; for(i, 1, m) scanf("%lld %lld %lld ", &e[i].u, &e[i].v, &e[i].w); sort(e + 1, e + m + 1, cmp); for(i, 1, m) { ll a = find(e[i].u), b = find(e[i].v), w = e[i].w; if(a != b) { ans += w; p[a] = b; e[i].used = true; if( ++ cnt = n - 1) break; } } return 0; }

强连通分量相关

单连通分量
ll dfn[N], low[N], timestamp, dep[N], scc_cnt, scc_size[N]; ll top, stk[N], id[N]; bool in_stk[N]; vector<ll> scc[N]; inline void tarjan(ll u) { in_stk[u] = true; dfn[u] = low[u] = ++ timestamp, stk[ ++ top] = u; for(rl i=h[u]; ~i; i = ne[i]) { ll v = e[i]; if(!dfn[v]) { tarjan(v); low[u] = min(low[u], low[v]); } else if(in_stk[v]) low[u] = min(low[u], low[v]); } if(low[u] == dfn[u]) { ll ele; scc_cnt ++ ; do { ele = stk[top -- ], in_stk[ele] = false; scc_size[scc_cnt] ++, scc[scc_cnt].push_back(ele); id[ele] = scc_cnt; } while(ele != u); } }
点双连通分量
ll dfn[N], low[N], dcc_cnt, dcc_size[N]; ll stk[N], top, id[N]; bool cut[N]; vector<ll> dcc[N]; inline void tarjan(ll u) { dfn[u] = low[u] = ++ timestamp, stk[ ++ top] = u; if(u == root && h[u] == -1) { dcc_cnt ++, dcc[dcc_cnt].push_back(u); return ; } ll cnt = 0; for(rl i=h[u]; ~i; i = ne[i]) { ll v = e[i]; if(!dfn[v]) { tarjan(v), low[u] = min(low[u], low[v]); if(dfn[u] <= low[v]) { ++ cnt; if(u != root || cnt > 1) cut[u] = true; ++ dcc_cnt; ll ele; do { ele = stk[ top -- ], dcc[dcc_cnt].push_back(ele); } while(u != v); dcc[dcc_cnt].push_back(u); } } else low[u] = min(low[u], dfn[v]); } }
边双连通分量
ll dfn[N], low[N], timestamp, id[N], dcc_cnt, dcc_size[N]; ll stk[N], top; inline void tarjan(ll u, ll from) { dfn[u] = low[u] = ++ timestamp; stk[ ++ top] = u; in_stk[u] = true; for(rl i=h[u]; ~i; i = ne[i]) { ll v = e[i]; if(!dfn[v]) { tarjan(v), low[u] = min(low[u], low[v]); if(dfn[u] < low[v]) is_bridge[i] = is_bridge[i ^ 1] = true; } else if(i != (from ^ 1)) low[u] = min(low[u], dfn[v]); } if(dfn[u] == low[u]) { ll ele; dcc_cnt ++ ; do { ele = stk[top -- ]; dcc_size[dcc_cnt] ++ ; dcc[dcc_cnt].push_back(ele); id[ele] = dcc_cnt; } whie(ele != u); }

匈牙利算法

一维
inline bool find(ll u) { for(rl i=h[u]; ~i; i = ne[i]) { ll v = e[i], t = match[v]; if(st[v]) continue; st[v] = true; if(!t || find(r)) {match[v] = u; return true; } } return false; } int main() { ll res = 0; for(i, 1, n) { memset(st, 0, sizeof st); if(find(i)) res ++ ; } return 0; }
二维
typedef pair<ll, ll> pll; pll match[N][N]; ll dx[] = {0, 1, 0, -1}, dy[] = {1, 0, -1, 0}; // 不同的方向数组可能不同 inline bool find(ll x, ll y) { for(i, 0, 3) { ll a = x + dx[i], b = y + dy[i]; if(a < 1 || a > n || b < 1 || b > m) continue; if(st[a][b] || g[a][b]) continue; // st 为是否查询过,g 为是否能够放置棋子 st[a][b] = true; auto = match[a][b]; if(!t.x || find(t.x, t.y)) { match[a][b] = {x, y}; return true; } } return false; }

差分约束

将式子转化为

xaxak

连接从 xaxa 一条长度为 k 的边

倍增法求LCA

code
#define ll long long #define rl register ll ll fa[N][M], dep[N]; inline void bfs() { memset(dep, 0x3f, sizeof dep); dep[0] = 0, dep[1] = 1; ll hh = 0, tt = -1; q[ ++ tt] = 1; while(hh <= tt) { ll u = q[hh ++ ]; for(rl i=h[u]; ~i; i = ne[i]) // 我的邻接表初始化为-1 { ll v = e[i]; if(dep[v] > dep[u] + 1) { dep[v] = dep[u] + 1; q[ ++ tt] = v; fa[v][0] = u; for(rl k=1; k < 21; ++ k) fa[v][k] = fa[fa[v][k-1]][k-1]; } } } } inline ll lca(ll a, ll b) { if(dep[a] < dep[b]) swap(a, b); for(rl i = 21; i >= 0; -- i) if(dep[fa[a][i]] >= dep[b]) a = fa[a][i]; if(a == b) return a; for(rl i=21; i >= 0; -- i) if(fa[a][i] != fa[b][i]) a = fa[a][i], b = fa[b][i]; return fa[a][0]; }

O(1) 复杂度访问LCA

code
ll dep[N], st[M][N], dfn[N], idx; inline ll get(ll a, ll b) { return dep[a] > dep[b] ? b : a; } inline void dfs(ll u, ll fa) { dep[u] = dep[fa] + 1, dfn[u] = ++ idx, st[0][idx] = fa; for(rl i=h[u]; ~i; i = ne[i]) { ll v = e[i]; if(v == fa) continue; dfs(v, u); } // 原因同上 } inline void init() { dfs(root, -1); for(rl j=0; j <= M; ++ j) for(rl i=1; i + (1 << j) - 1 <= n; ++ i) st[j][i] = get(st[j - 1][i], st[j - 1][i + (1 << j - 1)]); } inline ll lca(ll a, ll b) { if(a == b) return a; if((a = dfn[a]) > (b = dfn[b])) swap(a, b); ll l = __lg(b - a); return get(st[l][a + 1], st[l][b - (1 << l) + 1]); }

数据结构

树状数组(一维,单点修改,区间查询)

如若想要区间修改,单点查询,则只需要对存储的序列进行差分。然后存入树状数组

code
#define lowbit(x) (x & -x) inline void add(ll x, ll c) { for(; x <= n; x += lowbit(x)) tr[x] += c; } inline ll sum(ll x) { ll res = 0; for(; x; x -= lowbit(x)) res += tr[x]; return res; }

树状数组(二维,区间修改,区间查询)

树状数组在存储的时候同样也是存储差分序列。2023-10-12 16:20:18 星期四

code
#define lowbit(x) (x & -x) ll tr1[N], tr2[N]; inline void add(ll x, ll c) { ll i = x - 1; for(; x <= n; x += lowbit(x)) tr1[x] += c, tr2[x] += i * c; } inline void sum(ll x) { ll res = 0, resu = 0, i = x; for(; x; x -= lowbit(x)) res += tr1[x], resu += tr2[x]; return i * res - resu }

树链剖分

注意下面有打注释的地方不同题目可能不同,只是放了一些常见的操作。

code
inline void add(ll a, ll b) { e[ ++ tot] = {b, h[a]}, h[a] = tot; } inline void dfs1(ll u, ll p, ll d) { fa[u] = p, dep[u] = d, sz[u] = 1; for(rl i=h[u]; ~i; i = e[i].ne) { ll v = e[i].v; if(v == p) continue; dfs1(v, u, d + 1); sz[u] += sz[v]; if(sz[son[u]] < sz[v]) son[u] = v; } } inline void dfs2(ll u, ll t) { top[u] = t, id[u] = ++ cnt, nw[cnt] = w[u]; if(!son[u]) return ; dfs2(son[u], t); for(rl i=h[u]; ~i; i = e[i].ne) { ll v = e[i].v; if(v == son[u] || v == fa[u]) continue; dfs2(v, v); } } inline void update(ll u, ll l, ll r, ll k) { if(l <= tr[u].l && r >= tr[u].r) { tr[u].tag += k, tr[u].minn += k; // 依据题目而不同 return ; } pushdown(u); // 如果是区间修改且有懒标记的话,递归前要pushdown ll mid = tr[u].l + tr[u].r >> 1; // 注意别写成 l + r >> 1 if(l <= mid) update(u << 1, l, r, k); if(r > mid) update(u << 1 | 1, l, r, k); pushup(u); } inline ll query(ll u, ll l, ll r) { ll res = 1e17; // 依据题目求最大值最小值区间和而不同 if(l <= tr[u].l && r >= tr[u].r) return tr[u].minn; // 同上 pushdown(u); ll mid = tr[u].l + tr[u].r >> 1; // 注意同上 if(l <= mid) res = min(res, query(u << 1, l, r)); if(r > mid) res = min(res, query(u << 1 | 1, l, r)); // 同上 return res; } inline void upd_path(ll u, ll v, ll k) { while(top[u] != top[v]) { if(dep[top[u]] < dep[top[v]]) swap(u, v); update(1, id[top[u]], id[u], k); u = fa[top[u]]; } if(dep[u] < dep[v]) swap(u, v); update(1, id[v], id[u], k); } inline ll query_path(ll u, ll v) // 注释的查询路径的东西因题目而不同 { ll res = 1e17; // while(top[u] != top[v]) { if(dep[top[u]] < dep[top[v]]) swap(u, v); res = min(res, query(1, id[top[u]], id[u])); // query函数一样,但是求和还是最大值还是最小值不同 u = fa[top[u]]; } if(dep[u] < dep[v]) swap(u, v); res = min(res, query(1, id[v], id[u])); // 同上 return res; } inline void upd_tree(ll u, ll k) // 修改子树 { update(1, id[u], id[u] + sz[u] - 1, k); } inline ll que_tree(ll u) // 查询子树 { return query(1, id[u], id[u] + sz[u] - 1); }

__EOF__

本文作者carp
本文链接https://www.cnblogs.com/carp-oier/p/STL-of-carp.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   carp_oier  阅读(171)  评论(2编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示