Binary Indexed Tree - 세그먼트 트리보다 구현이 간단. 구간 합 쿼리에 특화.
- "구간 합 + 업데이트"
- BFS로 소스→싱크 경로 찾기 (잔여 용량 > 0 인 간선만)
- 경로 중 최소 용량(flow) 찾기
- 경로의 간선들에 flow 추가, 역방향 간선에 flow 빼기 (음수 유량)
- 경로 없을 때까지 반복
- 이분 매칭: 최대 유량의 특수한 형태 (용량 모두 1)
- Dinic 알고리즘: 정점이 많으면 에드몬드-카프보다 훨씬 빠름 (레벨 그래프 사용)
- 최소 컷(Min-Cut): 최대 유량 = 최소 컷 (Max-Flow Min-Cut Theorem)
- 역방향 간선: 반드시 만들어야 함! (취소 가능성 열어두기)
- "역순 쌍 개수" (Inversion Count)
- i번째 값: 원본 배열 값이 아니라, 특정 구간의 부분합을 저장
i & -i: 마지막 1의 위치 (LSB)를 이용하여 부모/자식 이동- Update:
idx에 더하고,idx += (idx & -idx)로 올라가며 갱신 - Query:
idx까지 합 구하고,idx -= (idx & -idx)로 내려가며 누적
- 1-indexed: 구현이 훨씬 편함 (0인덱스 쓰면 +1 해줘야 함)
- 구간 합 구하기:
query(R) - query(L-1) - Inversion Counting: 값의 범위가 크면 좌표 압축 후 펜윅 트리 사용
- 2차원 펜윅: 2중 for문으로 확장 가능 (구현 간단)
- 업데이트: O(logN)
- 쿼리: O(logN)
int tree[MAX];
int n;
// idx 위치에 delta 더하기
void update(int idx, int delta) {
for (; idx <= n; idx += idx & (-idx))
tree[idx] += delta;
}
// 1~idx 구간 합
long long sum(int idx) {
long long ret = 0;
for (; idx > 0; idx -= idx & (-idx))
ret += tree[idx];
return ret;
}
// [left, right] 구간 합
long long rangeSum(int left, int right) {
return sum(right) - sum(left - 1);
}idx & (-idx): 가장 낮은 1비트 (LSB)- 업데이트: LSB를 더하면서 위로 이동
- 쿼리: LSB를 빼면서 아래로 이동
예: n = 8
tree[1] = arr[1]
tree[2] = arr[1] + arr[2]
tree[3] = arr[3]
tree[4] = arr[1] + arr[2] + arr[3] + arr[4]
tree[5] = arr[5]
tree[6] = arr[5] + arr[6]
tree[7] = arr[7]
tree[8] = arr[1] + ... + arr[8]
tree = [0] * (n + 1)
def update(idx, delta):
while idx <= n:
tree[idx] += delta
idx += idx & (-idx)
def prefix_sum(idx):
ret = 0
while idx > 0:
ret += tree[idx]
idx -= idx & (-idx)
return ret
def range_sum(left, right):
return prefix_sum(right) - prefix_sum(left - 1)// 역순 쌍 개수 세기
long long countInversions(vector<int>& arr) {
// 좌표 압축 후 진행
long long inv = 0;
for (int i = arr.size() - 1; i >= 0; i--) {
inv += sum(arr[i] - 1); // 나보다 작은 수 중 이미 등장한 것
update(arr[i], 1);
}
return inv;
}- BOJ 2042 구간 합 구하기 ⭐⭐⭐
- BOJ 1517 버블 소트 ⭐⭐⭐ Inversion
- BOJ 12837 가계부 ⭐⭐
- BOJ 3653 영화 수집 ⭐⭐⭐