图论
存图
链式前向星
Code
| struct Graph{ |
| int cnt; |
| int head[MAXN]; |
| |
| Graph(){ |
| cnt = 0; |
| memset(head, 0, sizeof(head)); |
| } |
| |
| struct Edge{ |
| int to, next, dis; |
| }e[MAXN]; |
| |
| inline void Add(int u, int v, int w){ |
| e[++cnt].to = v; |
| e[cnt].dis = w; |
| e[cnt].next = head[u]; |
| head[u] = cnt; |
| } |
| }G; |
最短路
Floyed
Code
| void Floyed(){ |
| for(register int k = 1; k <= n; k++){ |
| for(register int i = 1; i <= n; i++){ |
| for(register int j = 1; j <= n; j++){ |
| if(k == i || k == j || i == j) continue; |
| if(map[i][j] > map[i][k] + map[k][j]) map[i][j] = map[i][k] + map[k][j]; |
| } |
| } |
| } |
| } |
SPFA
Code
| void Spfa(int u){ |
| queue<int> q; |
| memset(dis, 0x3f, sizeof(dis)); |
| |
| q.push(u), dis[u] = 0, vis[u] = true; |
| while(!q.empty()){ |
| int t = q.front(); |
| for(register int i = G.head[t]; i; i = G.e[i].next){ |
| int v = G.e[i].to; |
| if(dis[v] > dis[t] + G.e[i].dis){ |
| dis[v] = dis[t] + G.e[i].dis; |
| if(!vis[v]) q.push(v), vis[v] = true; |
| } |
| } |
| vis[t] = false; |
| q.pop(); |
| } |
| } |
堆优化 Dijkstra
Code
| struct Road{ |
| int pos, dis; |
| |
| bool operator < (const Road &a) const{ |
| return dis > a.dis; |
| } |
| }; |
| |
| priority_queue< Road, vector<Road>, greater<Road> > q; |
| |
| void Dijkstra(int u){ |
| memset(dis, 0x3f, sizeof(dis)); |
| |
| dis[u] = 0, q.push((Road){u, 0}); |
| while(!q.empty()){ |
| int t = q.top().pos; |
| q.pop(); |
| |
| if(vis[t]) continue; |
| vis[t] = true; |
| for(register int i = G.head[t]; i; i = G.e[i].next){ |
| int v = G.e[i].to; |
| if(dis[v] > dis[t] + G.e[i].dis){ |
| dis[v] = dis[t] + G.e[i].dis; |
| if(!vis[v]) q.push((Road){v, dis[v]}); |
| } |
| } |
| } |
| } |
最小生成树
Prim 算法
Code
| struct Road{ |
| int pos, dis; |
| |
| bool operator < (const Road &a) const{ |
| return dis > a.dis; |
| } |
| }; |
| |
| priority_queue< Road, vector<Road>, greater<Road> > q; |
| |
| void Prim(){ |
| memset(dis, 0x3f, sizeof(dis)); |
| dis[1] = 0; |
| q.push((Road){1, dis[1]}); |
| |
| while(!q.empty() && tot < n){ |
| int k = q.top().pos; |
| q.pop(); |
| |
| if(used[k]) continue; |
| tot++; |
| sum += dis[k]; |
| used[k] = true; |
| for(register int i = head[k]; i; i = e[i].next){ |
| int v = e[i].to; |
| if(dis[v] > e[i].dis){ |
| dis[v] = e[i].dis; |
| q.push((Road){v, dis[v]}); |
| } |
| } |
| } |
| } |
Kruskal 算法
Code
| struct Line{ |
| int from, to, dis; |
| }line[MAXM]; |
| |
| bool cmp(const Line &a, const Line &b){ |
| return a.dis < b.dis; |
| } |
| |
| struct Union_Set{ |
| int fa[MAXN]; |
| |
| void init(int n){ |
| for(register int i = 1; i <= n; i++) fa[i] = i; |
| } |
| |
| int Find(int x){ |
| return fa[x] == x ? x : fa[x] = Find(fa[x]); |
| } |
| }U; |
| |
| void Kruskal(){ |
| U.init(n); |
| sort(line + 1, line + 1 + m, cmp); |
| |
| for(register int i = 1; i <= m; i++){ |
| int u = line[i].from, v = line[i].to; |
| int fa_u = U.Find(u), fa_v = U.Find(v); |
| if(fa_u != fa_v){ |
| ++tot; |
| U.fa[fa_u] = fa_v; |
| G.Add(u, v, line[i].dis), G.Add(v, u, line[i].dis); |
| } |
| if(tot == n - 1) break; |
| } |
| } |
Tarjan
求强连通分量及缩点
Code
| void Tarjan(int u){ |
| low[u] = dfn[u] = ++num; |
| vis[u] = true; |
| stk[++top] = u; |
| |
| for(register int i = G.head[u]; i; i = G.e[i].next){ |
| int v = G.e[i].to; |
| if(!dfn[v]){ |
| Tarjan(v); |
| low[u] = min(low[u], low[v]); |
| } |
| else if(vis[v]) low[u] = min(low[u], dfn[v]); |
| } |
| |
| if(low[u] == dfn[v]){ |
| ++tot; int t; |
| do{ |
| t = stk[top--]; |
| vis[t] = false; |
| belong[t] = tot; |
| }while(t != u); |
| } |
| } |
| |
| void Rebuild(){ |
| G.cnt = 0; |
| memset(G.head, 0, sizeof(G.head)); |
| |
| for(register int i = 1; i <= m; i++){ |
| int u = from[i], v = to[i]; |
| if(belong[u] != belong[v]) G.Add(u, v); |
| } |
| } |
求割点
Code
| void Tarjan(int u){ |
| dfn[u] = low[u] = ++num; |
| int son = 0; |
| for(register int i = G.head[u]; i; i = G.e[i].next){ |
| int v = G.e[i].to; |
| if(!dfn[v]){ |
| ++son; |
| Tarjan(v); |
| low[u] = min(low[u], low[v]); |
| if(dfn[u] <= low[v]){ |
| if(son > 1 || u != root) cut[u] = true; |
| } |
| } |
| else low[u] = min(low[u], dfn[v]); |
| } |
| } |
求点双联通分量
Code
| void Tarjan(int u, int fa){ |
| dfn[u] = low[u] = ++num; |
| vis[u] = true, stk[++top] = u; |
| int son = 0; bool first = true; |
| |
| for(register int i = G.head[u]; i; i = G.e[i].next){ |
| int v = G.e[i].to; |
| if(first && v == fa){ |
| first = false; |
| continue; |
| } |
| if(!dfn[v]){ |
| ++son; |
| Tarjan(v, u); |
| low[u] = min(low[u], low[v]); |
| |
| if(dfn[u] <= low[v]){ |
| cut[u] = true; |
| ++tot; |
| point[tot].push_back(u); |
| int t; |
| do{ |
| t = stk[top--]; |
| point[tot].push_back(u); |
| }while(t != u); |
| } |
| } |
| else low[u] = min(low[u], dfn[v]); |
| } |
| |
| if(!fa && son == 1) cut[u] = false; |
| } |
二分图
匈牙利算法
Code
| bool dfs(int u){ |
| for(register int i = G.head[u]; i; i = G.e[i].next){ |
| int v = G.e[i].to; |
| if(!vis[v]){ |
| vis[v] = true; |
| if(!match[v] || dfs(match[v])){ |
| match[v] = u; |
| return true; |
| } |
| } |
| } |
| |
| return false; |
| } |
| |
| int Hungary(){ |
| int ans = 0; |
| |
| for(register int i = 1; i <= n; i++){ |
| memset(vis, 0, sizeof(vis)); |
| if(dfs(i)) ++ans; |
| } |
| |
| return ans; |
| } |
Kruskal 重构树
Code
| struct Line{ |
| int from, to, dis; |
| }line[MAXM]; |
| |
| bool cmp(const Line &a, const Line &b){ |
| return a.dis < b.dis; |
| } |
| |
| struct Union_Set{ |
| int fa[MAXN]; |
| |
| void init(int n){ |
| for(register int i = 1; i <= n; i++) fa[i] = i; |
| } |
| |
| int Find(int x){ |
| return fa[x] == x ? x : fa[x] = Find(fa[x]); |
| } |
| }U; |
| |
| void Kruskal(){ |
| int tot = 0; sum = n, G.cnt = 0; |
| memset(G.head, 0, sizeof(G.head)); |
| |
| U.init(n << 1); |
| sort(line + 1, line + 1 + m, cmp); |
| |
| for(register int i = 1; i <= m; i++){ |
| int u = line[i].from, v = line[i].to; |
| int fa_u = U.Find(u), fa_v = U.Find(v); |
| |
| if(fa_u != fa_v){ |
| ++tot; |
| val[++sum] = line[i].dis; |
| G.Add(fa_u, sum), G.Add(sum, fa_u); |
| G.Add(fa_v, sum), G.Add(sum, fa_v); |
| U.fa[fa_u] = U.fa[fa_v] = sum; |
| } |
| if(tot == n - 1) break; |
| } |
| } |
字符串
哈希
不用写。
KMP 算法
不会真有人到退役也搞不清楚 KMP 吧,不会吧不会吧。😋
Trie 树
字典树
Code
| struct Trie{ |
| int sz; |
| bool val[MAXN]; |
| int ch[MAXN][SIZE]; |
| |
| Trie(){ |
| sz = 0; |
| memset(ch, 0, sizeof(ch)); |
| memset(val, 0, sizeof(val)); |
| } |
| |
| void Insert(char *s){ |
| int u = 0, len = strlen(s + 1); |
| for(register int i = 1; i <= len; i++){ |
| int v = s[i] - 'a'; |
| if(!ch[u][v]) ch[u][v] = ++sz; |
| u = ch[u][v]; |
| } |
| val[u] = true; |
| } |
| |
| bool Search(char *s){ |
| int u = 0, len = strlen(s + 1); |
| for(register int i = 1; i <= len; i++){ |
| int v = s[i] - 'a'; |
| if(!ch[u][v]) return false; |
| u = ch[u][v]; |
| } |
| if(val[u]) return true; |
| else return false; |
| } |
| }T; |
01Trie
Code
| struct Trie{ |
| int sz; |
| bool val[MAXN]; |
| int ch[MAXN][2]; |
| |
| Trie(){ |
| sz = 0; |
| memset(ch, 0, sizeof(ch)); |
| memset(val, 0, sizeof(val)); |
| } |
| |
| void Insert(int num){ |
| int u = 0; |
| for(register int i = 31; i >= 0; i--){ |
| int v = (num >> i) & 1; |
| if(!ch[u][v]) ch[u][v] = ++sz; |
| u = ch[u][v]; |
| } |
| val[u] = true; |
| } |
| |
| int Search(int num){ |
| int u = 0, ans = 0; |
| for(register int i = 31; i >= 0; i--){ |
| int v = (num >> i) & 1; |
| if(ch[u][v ^ 1]) ans += 1 << i, u = ch[u][v ^ 1]; |
| else u = ch[u][v]; |
| } |
| |
| return ans; |
| } |
| }T; |
AC 自动机
树论
树链剖分
Code
| struct Graph{ |
| int cnt; |
| int head[MAXN]; |
| |
| struct Edge{ |
| int to, next; |
| }e[MAXN << 1]; |
| |
| void Add(int u, int v){ |
| e[++cnt].to = v; |
| e[cnt].next = head[u]; |
| head[u] = cnt; |
| } |
| }G; |
| |
| void dfs_deep(int rt, int father, int depth){ |
| size[rt] = 1, fa[rt] = father, deep[rt] = depth; |
| |
| int max_son = -1; |
| for(register int i = G.head[rt]; i; i = G.e[i].next){ |
| int v = G.e[i].to; |
| if(v == father) continue; |
| |
| dfs_deep(v, rt, depth + 1); |
| size[rt] += size[v]; |
| if(size[v] > max_son) son[rt] = v, max_son = size[v]; |
| } |
| } |
| |
| void dfs_top(int rt, int top_fa){ |
| dfn[rt] = ++num, top[rt] = top_fa, val[num] = data[rt]; |
| if(!son[rt]) return; |
| |
| dfs_top(son[rt], top_fa); |
| |
| for(register int i = G.head[rt]; i; i = G.e[i].next){ |
| int v = G.e[i].to; |
| if(!dfn[v]) dfs_top(v, v); |
| } |
| } |
| |
| struct Segment_Tree{ |
| struct Tree{ |
| int l, r; |
| LL sum; |
| LL lazy; |
| }tr[MAXN << 2]; |
| |
| inline int lson(int rt){ |
| return rt << 1; |
| } |
| |
| inline int rson(int rt){ |
| return rt << 1 | 1; |
| } |
| |
| inline void Pushup(int rt){ |
| tr[rt].sum = 1LL * (tr[lson(rt)].sum + tr[rson(rt)].sum) % p; |
| } |
| |
| void Build(int rt, int l, int r){ |
| tr[rt].l = l, tr[rt].r = r; |
| if(l == r){ |
| tr[rt].sum = val[l] % p; |
| return; |
| } |
| |
| int mid = (l + r) >> 1; |
| Build(lson(rt), l, mid); |
| Build(rson(rt), mid + 1, r); |
| |
| Pushup(rt); |
| } |
| |
| inline void Pushdown(int rt){ |
| if(tr[rt].lazy){ |
| tr[lson(rt)].lazy = 1LL * (tr[lson(rt)].lazy + tr[rt].lazy) % p; |
| tr[rson(rt)].lazy = 1LL * (tr[rson(rt)].lazy + tr[rt].lazy) % p; |
| tr[lson(rt)].sum = 1LL * (tr[lson(rt)].sum + (tr[lson(rt)].r - tr[lson(rt)].l + 1) % p * tr[rt].lazy % p) % p; |
| tr[rson(rt)].sum = 1LL * (tr[rson(rt)].sum + (tr[rson(rt)].r - tr[rson(rt)].l + 1) % p * tr[rt].lazy % p) % p; |
| tr[rt].lazy = 0; |
| } |
| } |
| |
| void Update_Add(int rt, int l, int r, int data){ |
| if(tr[rt].l >= l && tr[rt].r <= r){ |
| tr[rt].lazy = 1LL * (tr[rt].lazy + data) % p; |
| tr[rt].sum = 1LL * (tr[rt].sum + (tr[rt].r - tr[rt].l + 1) % p * data % p) % p; |
| return; |
| } |
| |
| Pushdown(rt); |
| |
| int mid = (tr[rt].l + tr[rt].r) >> 1; |
| if(l <= mid) Update_Add(lson(rt), l, r, data); |
| if(r > mid) Update_Add(rson(rt), l, r, data); |
| |
| Pushup(rt); |
| } |
| |
| LL Query_Sum(int rt, int l, int r){ |
| if(tr[rt].l >= l && tr[rt].r <= r) return tr[rt].sum; |
| |
| Pushdown(rt); |
| |
| LL ans = 0; |
| int mid = (tr[rt].l + tr[rt].r) >> 1; |
| if(l <= mid) ans = 1LL * (ans + Query_Sum(lson(rt), l, r)) % p; |
| if(r > mid) ans = 1LL * (ans + Query_Sum(rson(rt), l, r)) % p; |
| |
| return ans; |
| } |
| }S; |
| |
| void Update_Tree_Add(int x, int y, int data){ |
| while(top[x] != top[y]){ |
| if(deep[top[x]] < deep[top[y]]) swap(x, y); |
| S.Update_Add(1, dfn[top[x]], dfn[x], data); |
| x = fa[top[x]]; |
| } |
| if(deep[x] > deep[y]) swap(x, y); |
| S.Update_Add(1, dfn[x], dfn[y], data); |
| } |
| |
| LL Query_Tree_Sum(int x, int y){ |
| LL ans = 0; |
| |
| while(top[x] != top[y]){ |
| if(deep[top[x]] < deep[top[y]]) swap(x, y); |
| ans = 1LL * (ans + S.Query_Sum(1, dfn[top[x]], dfn[x])) % p; |
| x = fa[top[x]]; |
| } |
| if(deep[x] > deep[y]) swap(x, y); |
| ans = 1LL * (ans + S.Query_Sum(1, dfn[x], dfn[y])) % p; |
| |
| return ans; |
| } |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理