좋아!
백준 11012번 "EGG" 문제는
선생님은 학생들에게 점선으로 줄을 서도록 지시했다.
그 줄에는 N개의 알이 놓여 있으며, 각 알은[시작 위치, 끝 위치]구간을 점유한다.
선생님은 학생들에게 M개의 특정 위치를 짚게 하며,
그 위치에 몇 개의 알이 깔려 있는지를 확인하고자 한다.
[a, b] 형태로 주어지며, a ≤ bx에 대해, [a, b]에 x가 포함된 알의 개수를 묻는다.N M
a1 b1
a2 b2
...
aN bN
x1
x2
...
xM
q1
q2
...
qM
각 쿼리에 대해, 해당 위치에 존재하는 알의 개수를 출력
3 4
1 5
3 7
6 9
1
4
6
10
1
2
2
0
a, b, 쿼리 x)query(x)) 계산def update(bit, i, v):
while i < len(bit):
bit[i] += v
i += i & -i
def query(bit, i):
s = 0
while i > 0:
s += bit[i]
i -= i & -i
return s
# 입력
N, M = map(int, input().split())
eggs = [tuple(map(int, input().split())) for _ in range(N)]
queries = [int(input()) for _ in range(M)]
# 좌표 수집
coords = set()
for a, b in eggs:
coords.add(a)
coords.add(b + 1)
for x in queries:
coords.add(x)
# 좌표 압축
sorted_coords = sorted(coords)
coord_map = {v: i + 1 for i, v in enumerate(sorted_coords)}
bit = [0] * (len(coord_map) + 5)
# 알 반영: 시작 +1, 끝+1에 -1
for a, b in eggs:
update(bit, coord_map[a], 1)
update(bit, coord_map[b + 1], -1)
# 쿼리
for x in queries:
print(query(bit, coord_map[x]))
N = 3, M = 4
eggs = [
(1, 5),
(3, 7),
(6, 9)
]
queries = [1, 4, 6, 10]
→ 좌표 압축된 값:
sorted_coords = [1, 3, 4, 5, 6, 7, 8, 10]
coord_map = {1:1, 3:2, 4:3, 5:4, 6:5, 7:6, 8:7, 10:8}
→ BIT 업데이트 후:
→ 각 쿼리 결과는 query(coord_map[x])로 얻음
좋아!
우리가 방금 만든 이 Python 코드는 Binary Indexed Tree (BIT) 와 좌표 압축을 활용해서
간단한 구간 누적 문제를 푸는 구조야.
이제 이 코드를 메서드 단위로 나누고,
각 메서드 안에서 어떤 일이 어떻게 벌어지는지를
실제 예제 기준으로 한 줄씩 디버깅 해설해줄게.
def update(bit, i, v):
while i < len(bit):
bit[i] += v
i += i & -i
def query(bit, i):
s = 0
while i > 0:
s += bit[i]
i -= i & -i
return s
def compress_coordinates(eggs, queries):
coords = set()
for a, b in eggs:
coords.add(a)
coords.add(b + 1)
for x in queries:
coords.add(x)
sorted_coords = sorted(coords)
coord_map = {v: i + 1 for i, v in enumerate(sorted_coords)} # 1-based index
return coord_map
def process_eggs(bit, coord_map, eggs):
for a, b in eggs:
update(bit, coord_map[a], 1)
update(bit, coord_map[b + 1], -1)
def process_queries(bit, coord_map, queries):
results = []
for x in queries:
results.append(query(bit, coord_map[x]))
return results
그리고 메인 처리 코드:
N, M = map(int, input().split())
eggs = [tuple(map(int, input().split())) for _ in range(N)]
queries = [int(input()) for _ in range(M)]
coord_map = compress_coordinates(eggs, queries)
bit = [0] * (len(coord_map) + 5)
process_eggs(bit, coord_map, eggs)
results = process_queries(bit, coord_map, queries)
for r in results:
print(r)
3 4
1 5
3 7
6 9
1
4
6
10
update(bit, i, v)def update(bit, i, v):
while i < len(bit):
bit[i] += v
i += i & -i
i와 그 상위 노드에 v를 더해 누적update(bit, 1, 1)i = 1bit[1] += 1i = 1 + (1 & -1) = 2bit[2] += 1i = 4, bit[4] += 1i = 8, bit[8] += 1→ BIT는 구간 누적합을 만들기 위해 관련 노드 모두를 갱신
query(bit, i)def query(bit, i):
s = 0
while i > 0:
s += bit[i]
i -= i & -i
return s
query(bit, 4)s = 0s += bit[4]i = 0 → 종료s = 누적된 개수compress_coordinates(eggs, queries)def compress_coordinates(eggs, queries):
coords = set()
for a, b in eggs:
coords.add(a)
coords.add(b + 1)
for x in queries:
coords.add(x)
sorted_coords = sorted(coords)
coord_map = {v: i + 1 for i, v in enumerate(sorted_coords)} # 1-based index
return coord_map
수집한 좌표:
[1, 5+1=6, 3, 7+1=8, 6, 9+1=10, 1, 4, 6, 10] → 중복 제거 후 정렬
→ sorted_coords = [1, 3, 4, 5, 6, 7, 8, 10]
→ coord_map = {
1:1, 3:2, 4:3, 5:4, 6:5, 7:6, 8:7, 10:8
}
process_eggs(bit, coord_map, eggs)def process_eggs(bit, coord_map, eggs):
for a, b in eggs:
update(bit, coord_map[a], 1)
update(bit, coord_map[b + 1], -1)
+1, 끝+1 위치에 -1process_queries(bit, coord_map, queries)def process_queries(bit, coord_map, queries):
results = []
for x in queries:
results.append(query(bit, coord_map[x]))
return results
for r in results:
print(r)
출력:
1
2
2
0
| 메서드 | 설명 | 예제 처리 |
|---|---|---|
update | BIT에서 값 누적 | 알 시작/끝 처리 |
query | BIT에서 누적 개수 조회 | 쿼리 처리 |
compress_coordinates | 좌표 압축 | 1 |
process_eggs | 알의 범위 BIT에 누적 | +1/-1 방식 |
process_queries | 쿼리 위치 결과 조회 | 누적합 구함 |
좋아!
우리가 지금 디버깅하고 있는 코드는
알의 범위 [a, b] 를 Binary Indexed Tree (BIT)에 구간 누적합 형태로 기록하는 방식이야.
우리가 디버깅 중인 예제 입력은 다음과 같았어:
3 4
1 5
3 7
6 9
1
4
6
10
→ 중복 제거 후 정렬:
sorted_coords = [1, 3, 4, 5, 6, 7, 8, 10]
coord_map):| 원래 값 | 압축된 인덱스 (1부터 시작) |
|---|---|
| 1 | 1 |
| 3 | 2 |
| 4 | 3 |
| 5 | 4 |
| 6 | 5 |
| 7 | 6 |
| 8 | 7 |
| 10 | 8 |
[1, 5] → a=1, b=5[3, 7] → a=3, b=7[6, 9] → a=6, b=9a = 1 → coord_map[1] = 1 → update(bit, 1, 1)b+1 = 6 → coord_map[6] = 5 → update(bit, 5, -1)a = 3 → coord_map[3] = 2 → update(bit, 2, 1)b+1 = 8 → coord_map[8] = 7 → update(bit, 7, -1)a = 6 → coord_map[6] = 5 → update(bit, 5, 1)b+1 = 10 → coord_map[10] = 8 → update(bit, 8, -1)update(bit, 1, 1) # 알1 시작
update(bit, 5, -1) # 알1 끝 다음
update(bit, 2, 1) # 알2 시작
update(bit, 7, -1) # 알2 끝 다음
update(bit, 5, 1) # 알3 시작
update(bit, 8, -1) # 알3 끝 다음
bit[i]는 "i번 좌표에서 구간이 얼마나 시작되었는지"를 기록해update() 함수는 이 정보를 트리 형태로 누적query(x)는 "x좌표에서 총 몇 개의 알이 겹치는가"를 빠르게 알게 해줌| 압축된 좌표 인덱스 | 실제 좌표 | bit에 적용된 변화 |
|---|---|---|
| 1 | 1 | +1 (알1 시작) |
| 2 | 3 | +1 (알2 시작) |
| 5 | 6 | -1 (알1 끝), +1 (알3 시작) → 총 변화: 0 |
| 7 | 8 | -1 (알2 끝) |
| 8 | 10 | -1 (알3 끝) |
좋아!
이제 update(bit, i, v) 함수에 실제로 앞에서 구한 좌표 압축 인덱스와 값들을 넣고,
각 호출이 BIT 배열을 어떻게 바꾸는지 한 줄씩 디버깅해보자.
bit 길이는 len(coord_map) + 5len(coord_map) = 8 (압축된 좌표 수)bit 길이 대략 13 (0~12 인덱스), 인덱스 0은 사용 안 함bit = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
index: 0 1 2 3 4 5 6 7 8 9 10 11 12
앞서 구한 update 호출:
| 호출 순서 | 함수 호출 | 설명 |
|---|---|---|
| 1 | update(bit, 1, 1) | 알1 시작 |
| 2 | update(bit, 5, -1) | 알1 끝 다음 |
| 3 | update(bit, 2, 1) | 알2 시작 |
| 4 | update(bit, 7, -1) | 알2 끝 다음 |
| 5 | update(bit, 5, 1) | 알3 시작 |
| 6 | update(bit, 8, -1) | 알3 끝 다음 |
i = 1, v = 1while i < len(bit): # i=1 < 13
bit[1] += 1 # bit[1] = 0 + 1 = 1
i += i & -i # i = 1 + (1 & -1) = 1 + 1 = 2
while i < len(bit): # i=2 < 13
bit[2] += 1 # bit[2] = 0 + 1 = 1
i += i & -i # i = 2 + (2 & -2) = 2 + 2 = 4
while i < len(bit): # i=4 < 13
bit[4] += 1 # bit[4] = 0 + 1 = 1
i += i & -i # i = 4 + (4 & -4) = 4 + 4 = 8
while i < len(bit): # i=8 < 13
bit[8] += 1 # bit[8] = 0 + 1 = 1
i += i & -i # i = 8 + (8 & -8) = 8 + 8 = 16
while i < len(bit): # i=16 >= 13, 종료
bit 배열 상태bit = [0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0]
i = 5, v = -1while i < len(bit): # i=5 < 13
bit[5] += -1 # bit[5] = 0 + (-1) = -1
i += i & -i # i = 5 + (5 & -5)
# 5 = 0101, -5 = 1011 (2의 보수), 5 & -5 = 1
# i = 5 + 1 = 6
while i < len(bit): # i=6 < 13
bit[6] += -1 # bit[6] = 0 + (-1) = -1
i += i & -i # i = 6 + (6 & -6)
# 6 = 0110, -6 = 1010, 6 & -6 = 2
# i = 6 + 2 = 8
while i < len(bit): # i=8 < 13
bit[8] += -1 # bit[8] = 1 + (-1) = 0
i += i & -i # i = 8 + 8 = 16
while i < len(bit): # i=16 >= 13, 종료
bit 배열 상태bit = [0, 1, 1, 0, 1, -1, -1, 0, 0, 0, 0, 0, 0]
i = 2, v = 1while i < len(bit): # i=2 < 13
bit[2] += 1 # bit[2] = 1 + 1 = 2
i += i & -i # i = 2 + 2 = 4
while i < len(bit): # i=4 < 13
bit[4] += 1 # bit[4] = 1 + 1 = 2
i += i & -i # i = 4 + 4 = 8
while i < len(bit): # i=8 < 13
bit[8] += 1 # bit[8] = 0 + 1 = 1
i += i & -i # i = 8 + 8 = 16
while i < len(bit): # i=16 >= 13 종료
bit 배열 상태bit = [0, 1, 2, 0, 2, -1, -1, 0, 1, 0, 0, 0, 0]
i = 7, v = -1while i < len(bit): # i=7 < 13
bit[7] += -1 # bit[7] = 0 + (-1) = -1
i += i & -i # i = 7 + (7 & -7)
# 7 = 0111, -7 = 1001, 7 & -7 = 1
# i = 7 + 1 = 8
while i < len(bit): # i=8 < 13
bit[8] += -1 # bit[8] = 1 + (-1) = 0
i += i & -i # i = 8 + 8 = 16
while i < len(bit): # i=16 >= 13 종료
bit 배열 상태bit = [0, 1, 2, 0, 2, -1, -1, -1, 0, 0, 0, 0, 0]
i = 5, v = 1while i < len(bit): # i=5 < 13
bit[5] += 1 # bit[5] = -1 + 1 = 0
i += i & -i # i = 5 + 1 = 6
while i < len(bit): # i=6 < 13
bit[6] += 1 # bit[6] = -1 + 1 = 0
i += i & -i # i = 6 + 2 = 8
while i < len(bit): # i=8 < 13
bit[8] += 1 # bit[8] = 0 + 1 = 1
i += i & -i # i = 8 + 8 = 16
while i < len(bit): # i=16 >= 13 종료
bit 배열 상태bit = [0, 1, 2, 0, 2, 0, 0, -1, 1, 0, 0, 0, 0]
i = 8, v = -1while i < len(bit): # i=8 < 13
bit[8] += -1 # bit[8] = 1 + (-1) = 0
i += i & -i # i = 8 + 8 = 16
while i
< len(bit): # i=16 >= 13 종료
---
### 업데이트 후 최종 `bit` 배열 상태
```plaintext
bit = [0, 1, 2, 0, 2, 0, 0, -1, 0, 0, 0, 0, 0]
| 인덱스 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | ... |
|---|---|---|---|---|---|---|---|---|---|---|
| 값 | 0 | 1 | 2 | 0 | 2 | 0 | 0 | -1 | 0 | ... |