Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
- **그 외 모든 식별자**는 **소문자 `snake_case`** 사용 (STL 스타일):
- struct, class, namespace, function, variable, file name
- 예시: `struct fenwick_tree`, `namespace fast_io`, `solve_case`, `add_edge`
- **간결하지만 설명적**: 이름은 타이핑 속도를 위해 최대 7자 이내로 짧아야 함. 동시에 압박 속에서도 읽을 수 있을 정도로는 명확해야 함.
- **간결하지만 설명적**: 이름은 타이핑 속도를 위해 최대 12자 이내로 짧아야 함. 동시에 압박 속에서도 읽을 수 있을 정도로는 명확해야 함.
- **Good**: `cnt`, `idx`, `res`, `nxt`, `vis`, `dist`.
- **Bad**: `number_of_elements`, `adjacency_list`, `calculated_distance`.

Expand Down
2 changes: 1 addition & 1 deletion scripts/test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ cd "$root_dir"
out_dir=$(mktemp -d)
trap 'rm -rf "$out_dir"' EXIT

for src in tests/1-ds/test_*.cpp; do
for src in tests/1-ds/test_*.cpp tests/2-graph/test_*.cpp; do
bin="$out_dir/$(basename "${src%.cpp}")"
echo "[build] $src"
g++ -std=c++17 -O2 -pipe "$src" -o "$bin"
Expand Down
123 changes: 62 additions & 61 deletions src/2-graph/bcc.cpp
Original file line number Diff line number Diff line change
@@ -1,69 +1,70 @@
#include "../common/common.hpp"

// A Biconnected Component (BCC) is a subset of vertices in an undirected graph that satisfies the following conditions:
// (1) If you delete any vertex from a subset, the remaining vertices are connected to each other.
// (2) Adding other vertices to this subset does not satisfy (1). (This is the largest set that satisfies (1))
// TIME COMPLEXITY: O(V + E)
// what: biconnected components + articulation points/edges (undirected).
// time: O(n+m); memory: O(n+m)
// constraint: 1-indexed; no self-loops; recursion depth O(n).
// usage: bcc g; g.init(n); g.add(u,v); g.run(); // g.bccs, g.ap, g.ae
struct bcc {
int n, tim;
vector<vector<pii>> adj;
vector<int> dfn, low, ap, st;
vector<pii> ed, ae;
vector<vector<pii>> bccs;

// A vertex at which the graph is divided into two or more components when the vertex is removed is called a 'articulation point'.
// After decomposing the graph into BCCs, vertices belonging to two or more BCCs are articulation point.
// Similarly, a edge at which the graph is divided into two or more components when the edge is removed is called a 'articulation edge'.
// For all tree edges on a dfs spanning tree, if tmp > dfsn[now], the edge { now, next } is a articulation edge.

const int MAXV = 101010;
int n, m;
vector<int> adj[MAXV];
vector<vector<pii>> bcc;
set<int> aPoint;
set<pii> aEdge;
void input() {
cin >> n >> m;
for (int i = 0; i < m; i++) {
int u, v;
cin >> u >> v;
adj[u].push_back(v);
adj[v].push_back(u);
void init(int n_) {
n = n_;
tim = 0;
adj.assign(n + 1, {});
dfn.assign(n + 1, -1);
low.assign(n + 1, 0);
ap.clear();
ae.clear();
st.clear();
ed.clear();
bccs.clear();
}
void add(int u, int v) {
int id = sz(ed);
ed.push_back({u, v});
adj[u].push_back({v, id});
adj[v].push_back({u, id});
}
}
int dfsn[MAXV], dCnt;
stack<pii> stk;
int dfs(int now, int prv) {
int ret = dfsn[now] = ++dCnt;
int childCnt = 0;
for (int next : adj[now]) {
if (next == prv) continue;
// If an edge { now, next } has not yet been visited, puts an edge on the stack.
if (dfsn[now] > dfsn[next]) stk.push({now, next});
// Back edge
if (dfsn[next] != -1) ret = min(ret, dfsn[next]);
// Tree edge
else {
childCnt++;
int tmp = dfs(next, now);
ret = min(ret, tmp);
if (prv != -1 && tmp >= dfsn[now])
aPoint.insert(now);
if (tmp > dfsn[now])
aEdge.insert({min(now, next), max(now, next)});
// If next cannot go to ancestor node of now, find BCC
if (tmp >= dfsn[now]) {
vector<pii> nowBCC;
while (true) {
pii t = stk.top();
stk.pop();
nowBCC.push_back(t);
if (t == make_pair(now, next)) break;
void dfs(int v, int pe) {
dfn[v] = low[v] = ++tim;
int ch = 0;
for (auto [to, id] : adj[v]) {
if (id == pe) continue;
if (dfn[to] != -1) {
// edge: back edge to ancestor.
low[v] = min(low[v], dfn[to]);
if (dfn[to] < dfn[v]) st.push_back(id);
continue;
}
st.push_back(id);
ch++;
dfs(to, id);
low[v] = min(low[v], low[to]);
if (pe != -1 && low[to] >= dfn[v]) ap.push_back(v);
if (low[to] > dfn[v]) ae.push_back({min(v, to), max(v, to)});
if (low[to] >= dfn[v]) {
vector<pii> comp;
while (1) {
int eid = st.back();
st.pop_back();
comp.push_back(ed[eid]);
if (eid == id) break;
}
bcc.push_back(nowBCC);
bccs.push_back(comp);
}
}
if (pe == -1 && ch > 1) ap.push_back(v);
}
void run() {
for (int v = 1; v <= n; v++)
if (dfn[v] == -1) dfs(v, -1);
sort(all(ap));
ap.erase(unique(all(ap)), ap.end());
sort(all(ae));
ae.erase(unique(all(ae)), ae.end());
}
if (prv == -1 && childCnt > 1)
aPoint.insert(now);
return ret;
}
void getBCC() {
memset(dfsn, -1, sizeof(dfsn));
for (int v = 1; v <= n; v++)
if (dfsn[v] == -1) dfs(v, -1);
}
};
71 changes: 0 additions & 71 deletions src/2-graph/dijkstra_bellman_ford_floyd_warshall.cpp

This file was deleted.

104 changes: 58 additions & 46 deletions src/2-graph/euler_circuit.cpp
Original file line number Diff line number Diff line change
@@ -1,54 +1,66 @@
#include "../common/common.hpp"

// Hierholzer's Algorithm
// INPUT: Given a undirected graph.
// OUTPUT: Print the path of the Euler circuit of the graph.
// Euler Path is a path in a finite graph that visits every edge exactly once.
// Similarly, an Euler Circuit is an Euler Path that starts and ends on the same vertex.
// TIME COMPLEXITY: O(VE)
const int MAXV = 1010;
int n, adj[MAXV][MAXV], nxt[MAXV];
vector<int> eulerCircult;
void input() {
cin >> n;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
cin >> adj[i][j];
}
// what: Euler circuit via Hierholzer (undirected multigraph, matrix).
// time: O(n^2+m); memory: O(n^2)
// constraint: 1-indexed; all nonzero-degree nodes connected.
// usage: ecir g; g.init(n); g.add(u,v); if (g.can()) auto path=g.run(1);
struct ecir {
int n;
vector<vector<int>> adj;
vector<int> nxt, path;

void init(int n_) {
n = n_;
adj.assign(n + 1, vector<int>(n + 1));
nxt.assign(n + 1, 1);
path.clear();
}
void add(int u, int v, int c = 1) {
if (u == v) adj[u][u] += 2 * c;
else adj[u][v] += c, adj[v][u] += c;
}
}
int doesEulerCircuitExist() {
// If the degree of all nodes in the graph is even, then an euler circuit exists.
// Otherwise, the euler circuit does not exist.
// We can do similar way to determine the existence of euler path.
// If only two vertices have odd degree, than an eular path exists. Otherwise, the euler path does not exist.
for (int i = 1; i <= n; i++) {
int deg = 0;
for (int j = 1; j <= n; j++) {
deg += adj[i][j];
bool can() {
vector<int> deg(n + 1);
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++) deg[i] += adj[i][j];
for (int i = 1; i <= n; i++)
if (deg[i] & 1) return 0;
int s = 0;
for (int i = 1; i <= n; i++)
if (deg[i]) {
s = i;
break;
}
if (!s) return 1;
vector<int> vis(n + 1);
queue<int> q;
q.push(s);
vis[s] = 1;
while (!q.empty()) {
int v = q.front();
q.pop();
for (int i = 1; i <= n; i++)
if (adj[v][i] && !vis[i]) vis[i] = 1, q.push(i);
}
if (deg & 1) return 0;
for (int i = 1; i <= n; i++)
if (deg[i] && !vis[i]) return 0;
return 1;
}
return 1;
}
void dfs(int now) {
for (int &x = nxt[now]; x <= n; x++) {
while (x <= n && adj[now][x]) {
adj[now][x]--;
adj[x][now]--;
dfs(x);
void dfs(int v) {
for (int &i = nxt[v]; i <= n; i++) {
while (i <= n && adj[v][i]) {
adj[v][i]--;
adj[i][v]--;
dfs(i);
}
}
path.push_back(v);
}
eulerCircult.push_back(now);
}
int main() {
input();
if (!doesEulerCircuitExist()) {
cout << -1;
return 0;
vector<int> run(int s = 1) {
for (int i = 1; i <= n; i++) nxt[i] = 1;
path.clear();
dfs(s);
reverse(all(path));
return path;
}
for (int i = 1; i <= n; i++) nxt[i] = 1;
dfs(1);
for (auto i : eulerCircult)
cout << i << ' ';
}
};
Loading