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
62 changes: 32 additions & 30 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,65 +3,67 @@
이 저장소는 competitive programming 참고 라이브러리입니다 (ICPC, CF, BOJ 등).
여기에 코드를 추가하거나 수정할 때는, 대회에서의 사용성을 최우선으로 최적화하세요.

## 핵심 목표 (우선순위 순)
1. **빠르게 타이핑하고 붙여넣기**: 짧은 코드 길이. 실용적인 API와 파일 구조.
2. **빠르고 메모리 효율적**: 검증된 시간 복잡도, 낮은 상수, 최소한의 할당.
3. **압박 속에서도 읽기 쉬움**: 단순한 제어 흐름, 명확한 불변식, 친절한 주석.
4. **템플릿 친화적**: 최소한의 수정으로 다양한 변형 문제들에 쉽게 재사용.
## 핵심 목표
- **빠르게 타이핑하고 붙여넣기**: 짧은 코드 길이. 실용적인 API와 파일 구조.
- **빠르고 메모리 효율적**: 검증된 시간 복잡도, 낮은 상수, 최소한의 할당.
- **압박 속에서도 읽기 쉬움**: 단순한 제어 흐름, 명확한 불변식, 친절한 주석.
- **템플릿 친화적**: 최소한의 수정으로 다양한 변형 문제들에 적용 가능.

## 저장소 표준 코드 스타일 (필수)
## 코드 스타일 및 규칙

### Naming
- **상수** (예: `constexpr`, 전역 `const`, 고정 파라미터): `UPPER_SNAKE_CASE`.
- 예시: `constexpr int MOD = 1e9 + 7;`, `const int INF = 1e9;`
- **그 외 모든 식별자**는 **소문자 `snake_case`** 사용 (STL 스타일):
- struct, class, namespace, function, variable, file name
- 예시: `struct fenwick_tree`, `namespace fast_io`, `solve_case`, `add_edge`
- **간결하지만 설명적**: 이름은 타이핑 속도를 위해 최대 12자 이내로 짧아야 함. 동시에 압박 속에서도 읽을 수 있을 정도로는 명확해야 함.
- struct, class, namespace, function, variable
- 예시: `struct fenwick_tree`, `solve_case`, `add_edge`
- **간결하지만 설명적**: 이름은 최대 12자 이내로 짧아야 함. 동시에 압박 속에서도 읽을 수 있을 정도로 명확해야 함.
- **Good**: `cnt`, `idx`, `res`, `nxt`, `vis`, `dist`.
- **Bad**: `number_of_elements`, `adjacency_list`, `calculated_distance`.

### Types
### Types, Macros
- 오버플로 디버깅을 줄이기 위해 기본적으로 `ll`을 선호. `int`는 명확히 안전하고 이점이 있을 때만 사용 (메모리, bitset 인덱싱, 배열 인덱스, 빡센 제약 등).
- 암묵적 축소 변환을 피하기. 타입을 섞어 쓸 때 경계에서는 명시적으로 캐스팅.
- 코드 길이, 타이핑을 줄이기 위해 `common.hpp`의 alias (예: `pll`, `all(x)`)를 적극적으로 사용.
- 코드 길이, 타이핑을 줄이기 위해 `common.hpp`의 alias (예: `pll`, `all(x)`)를 항상 사용.

## 코딩 규칙
### 기타
- 무거운 추상화보다 직관적인 구현을 선호.
- 불필요한 동적 다형성, 복잡한 메타프로그래밍, 과도하게 공학적인 설계를 피하기.
- ICPC 팀 노트에는 길이 제한이 있음. 코드는 가능한 한 간결하게 작성.
- ICPC 팀 노트에는 길이 제한이 있음. 코드는 가능한 한 짧고 간결하게 작성.
- namespace를 사용하지 말고, struct로 대체할 것.

## 주석과 문서화 (필수)
일관된 형식으로 친절하고 신호가 높은 문서를 작성하세요.
특히, 난이도가 높은 알고리즘/자료구조일수록 기억이 희미할 확률이 높으니 자세히 설명하세요.
## 주석과 문서화
일관된 형식으로 친절하고 신호가 높은 문서를 작성.
특히, 난이도가 높은 알고리즘/자료구조일수록 자세하게 설명.

### 1) 모듈 헤더 (재사용 가능한 각 컴포넌트마다)
### 모듈 헤더 (재사용 가능한 각 컴포넌트마다)
상단 근처에 헤더 주석을 추가:
- what: 단순히 알고리즘/자료구조의 이름을 적는 것이 아닌, 해당 구현체가 사용되는 맥락과 역할을 black box로 작성.
- time; memory
- constraint
- usage
- what: 구현체애 대한 설명. 단순히 알고리즘/자료구조의 이름을 적는 것이 아닌, 해당 구현체가 사용되는 context, 해결할 수 있는 문제를 black box로 작성.
- time: 시간복잡도; memory: 공간복잡도
- constraint: 구현 세부사항, 코드에 내재된 가정, 주의사항 등.
- usage: 사용 예시 코드

### 2) 인라인 주석 (코드 전반)
### 인라인 주석 (코드 전반)
짧고 일관된 한 줄 주석을 넉넉하게 달기:
- 출력 (`// result: ...` = 주요 함수 반환값의 의미)
- 의도 (`// goal: ...`)
- 불변식 (`// invariant: ...` = 루프/알고리즘 전반에서 항상 참이어야 함)
- 엣지 케이스 (`// edge: ...`)
- 까다로운 추론 (`// why: ...`)

긴 문단 몇 개보다, 작은 주석을 많이 두는 걸 선호.

## Formatting (필수)
## Formatting
변경 후에는 항상 실행:
- `scripts/format.sh`

포맷되지 않은 코드를 커밋하지 마세요.

## 테스트 (권장)
- 템플릿 파일과 1:1 대응되는 테스트 파일을 둔다. 예: `src/1-ds/erasable_pq.cpp` -> `tests/1-ds/test_erasable_pq.cpp`
- 파일명 규칙: `test_{*}.cpp` (길이 제한 없음)
## 테스트
- 템플릿 파일과 1:1 대응되는 테스트 파일을 둔다.
- 예: `src/1-ds/erasable_pq.cpp` -> `tests/1-ds/test_erasable_pq.cpp`
- 파일명 규칙: `test_{*}.cpp`
- 테스트는 단일 실행 파일로 작성하고 외부 프레임워크 없이 assert/직접 비교를 사용
- 모든 버그를 잡아낼 수 있도록 특별히 주의를 기울여 robust한 테스트 코드를 작성
- 랜덤 + 나이브 비교, 엣지 케이스는 반드시 포함
- 랜덤은 재현 가능하도록 고정 seed 사용 (필요 시 seed 출력)
- 실행은 `scripts/test.sh`로 일괄, 가능하면 로컬에서도 실행
- 다음은 기본 테스트 대상에서 제외: `1-ds/pbds.cpp`
- 랜덤은 재현 가능하도록 고정 seed 사용
- 실행은 `scripts/test.sh`로 일괄
3 changes: 2 additions & 1 deletion scripts/test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ cd "$root_dir"
out_dir=$(mktemp -d)
trap 'rm -rf "$out_dir"' EXIT

for src in tests/1-ds/test_*.cpp tests/2-graph/test_*.cpp; do
for src in tests/1-ds/test_*.cpp tests/2-graph/test_*.cpp tests/3-tree/test_*.cpp \
tests/4-optimizations/test_*.cpp tests/5-string/test_*.cpp; do
bin="$out_dir/$(basename "${src%.cpp}")"
echo "[build] $src"
g++ -std=c++17 -O2 -pipe "$src" -o "$bin"
Expand Down
51 changes: 51 additions & 0 deletions src/3-tree/centroid_decomp.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
#include "../common/common.hpp"

// what: centroid decomposition for tree, builds centroid parent/children for path queries.
// time: O(n log n); memory: O(n)
// constraint: 1-indexed tree.
// usage: cen_decomp cd; cd.init(n); cd.add(u,v); cd.build(); int p=cd.par[v];
struct cen_decomp {
int n;
vector<vector<int>> adj, chd;
vector<int> par, siz;
vector<char> used;

void init(int n_) {
n = n_;
adj.assign(n + 1, {});
chd.assign(n + 1, {});
par.assign(n + 1, 0);
siz.assign(n + 1, 0);
used.assign(n + 1, 0);
}
void add(int u, int v) {
adj[u].push_back(v);
adj[v].push_back(u);
}
int get_sz(int v, int p) {
// goal: subtree sizes for centroid search.
siz[v] = 1;
for (int to : adj[v]) {
if (to == p || used[to]) continue;
siz[v] += get_sz(to, v);
}
return siz[v];
}
int get_cent(int v, int p, int tot) {
// invariant: siz is valid for current component.
for (int to : adj[v]) {
if (to == p || used[to]) continue;
if (siz[to] > tot / 2) return get_cent(to, v, tot);
}
return v;
}
void build(int v = 1, int p = 0) {
int tot = get_sz(v, p);
int c = get_cent(v, p, tot);
par[c] = p;
if (p) chd[p].push_back(c);
used[c] = 1;
for (int to : adj[c])
if (!used[to]) build(to, c);
}
};
69 changes: 69 additions & 0 deletions src/3-tree/hld.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
#include "../1-ds/segment_tree.cpp"

// what: heavy-light decomposition for path sum on tree (node values).
// time: build O(n), update/query O(log^2 n); memory: O(n)
// constraint: 1-indexed tree.
// usage: hld_tree h; h.init(n); h.add(u,v); h.build(1); h.set(v,x); ll s=h.qry(u,v);
struct hld_tree {
seg_tree seg;

int n, tim;
vector<vector<int>> adj;
vector<int> par, dep, siz, heavy, top, in;

void init(int n_) {
n = n_;
tim = 0;
adj.assign(n + 1, {});
par.assign(n + 1, 0);
dep.assign(n + 1, 0);
siz.assign(n + 1, 0);
heavy.assign(n + 1, -1);
top.assign(n + 1, 0);
in.assign(n + 1, 0);
}
void add(int u, int v) {
adj[u].push_back(v);
adj[v].push_back(u);
}
int dfs_sz(int v, int p) {
// goal: compute subtree sizes and heavy child.
par[v] = p;
dep[v] = dep[p] + 1;
siz[v] = 1;
int mx = 0;
for (int to : adj[v]) {
if (to == p) continue;
int s = dfs_sz(to, v);
siz[v] += s;
if (s > mx) mx = s, heavy[v] = to;
}
return siz[v];
}
void dfs_hld(int v, int h) {
// invariant: top[v] is head of heavy path.
top[v] = h;
in[v] = ++tim;
if (heavy[v] != -1) dfs_hld(heavy[v], h);
for (int to : adj[v])
if (to != par[v] && to != heavy[v]) dfs_hld(to, to);
}
void build(int root = 1) {
dfs_sz(root, 0);
dfs_hld(root, root);
vector<ll> a(n + 1, 0);
seg.build(a);
}
void set(int v, ll val) { seg.set(in[v], val); }
ll qry(int a, int b) const {
ll ret = 0;
while (top[a] != top[b]) {
if (dep[top[a]] < dep[top[b]]) swap(a, b);
ret += seg.query(in[top[a]], in[a]);
a = par[top[a]];
}
if (dep[a] > dep[b]) swap(a, b);
ret += seg.query(in[a], in[b]);
return ret;
}
};
51 changes: 51 additions & 0 deletions src/3-tree/lca_sparse_table.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
#include "../common/common.hpp"

// what: LCA via binary lifting for rooted tree.
// time: build O(n log n), query O(log n); memory: O(n log n)
// constraint: 1-indexed tree.
// usage: lca_sparse l; l.init(n); l.add(u,v); l.build(1); int w=l.lca(u,v);
struct lca_sparse {
int n, lg;
vector<vector<int>> adj, up;
vector<int> dep;

void init(int n_) {
n = n_;
lg = 1;
while ((1 << lg) <= n) lg++;
adj.assign(n + 1, {});
up.assign(lg, vector<int>(n + 1, 0));
dep.assign(n + 1, 0);
}
void add(int u, int v) {
adj[u].push_back(v);
adj[v].push_back(u);
}
void dfs(int v, int p) {
// goal: set parent and depth.
up[0][v] = p;
dep[v] = dep[p] + 1;
for (int to : adj[v])
if (to != p) dfs(to, v);
}
void build(int root = 1) {
dfs(root, 0);
for (int k = 0; k + 1 < lg; k++)
for (int v = 1; v <= n; v++)
up[k + 1][v] = up[k][up[k][v]];
}
int lca(int a, int b) const {
if (dep[a] < dep[b]) swap(a, b);
int diff = dep[a] - dep[b];
for (int k = 0; k < lg; k++)
if (diff & (1 << k)) a = up[k][a];
if (a == b) return a;
for (int k = lg - 1; k >= 0; k--) {
if (up[k][a] != up[k][b]) {
a = up[k][a];
b = up[k][b];
}
}
return up[0][a];
}
};
39 changes: 0 additions & 39 deletions src/3-tree/tree_cd.cpp

This file was deleted.

Loading