Skip to content

Using algorithm builtins

This guide covers the builtins that replace the most common copied contest templates.

Create a graph, add edges, then call the algorithm you need.

g = graph(n) # directed, unweighted
g = graph(n, undirected = true) # undirected, unweighted
wg = wgraph(n) # directed, weighted
wg = wgraph(n, undirected = true) # undirected, weighted

Add edges:

g.add(u, v)
wg.add(u, v, w)
res = g.bfs(src)
println res.dist[dst]

res.dist[i] is -1 when i is unreachable.

res = wg.dijkstra(src)
println res.dist[dst] == INF ? -1 : res.dist[dst]
res = wg.zero_one_bfs(src)
println res.dist[dst]
wg.bellman_ford(src) # distances + negative-cycle flag
wg.floyd() # all-pairs shortest paths
g.topo() # topological order
g.scc() # strongly connected components
g.bridges() # bridge edges
g.articulation_points() # articulation points
g.bipartite() # 2-coloring or failure marker
g.bipartite_matching(L) # maximum matching on [0, L) -> [L, n)
wg.mst() # minimum spanning tree

For matching, keep the partition explicit:

res = g.bipartite_matching(L)
println res.size
println res.match_left
input:
R, C: int
grid: matrix[char][R][C]
g = grid_graph(grid)

Use 4 for orthogonal movement and 8 when diagonal moves are allowed. The explicit form is grid_graph(grid, dirs = 8).

ans = bsearch(lo, hi, |mid| feasible(mid))

This returns the first integer in [lo, hi] where the predicate is true.

ans = bsearch_last(lo, hi, |mid| can_keep(mid))

This returns the last integer in [lo, hi] where the predicate is true.

lower(a, x)
upper(a, x)

Use these after sorting an array when you need counts, insertion points, or range boundaries.

d = dsu(n)
d.unite(u, v)
d.same(u, v)
d.find(u)
d.size(u)
d.components()

Use DSU for connectivity under unions, Kruskal-style workflows, and component counting.

t = tree(n)
t.add(u, v)
t.build(0)
println t.lca(a, b)
println t.dist(a, b)

Use tree(...) when the input is a tree and you need LCA, distances, subtree sizes, or ancestor jumps without pasting a binary-lifting template.

st = segtree(n, op = max)
st = segtree(a, op = sum)

Supported reducers are sum, max, min, gcd, and prod.

st.update(i, val)
st.query(l, r)

Use a segment tree when you need point updates plus range queries.

b = bit(n)
b.add(i, val)
b.sum(l, r)
b.lower_bound(target)

Use a Fenwick tree when you only need prefix/range sums with point updates.

sp = sparse(a, op = min)
sp.query(l, r)

Use a sparse table for static arrays with idempotent operations such as min, max, or gcd.

  • Graph shortest paths on unweighted edges: bfs
  • Graph shortest paths on non-negative weighted edges: dijkstra
  • Connectivity under unions: dsu
  • Point updates + range queries: segtree
  • Point updates + sum queries only: bit
  • Static RMQ/GCD-style queries: sparse