에그 변형

류한선·2025년 6월 20일

실기연습-2

목록 보기
10/95

좋아!
백준 11012번 "EGG" 문제는

  • 이벤트 정렬 (스위핑)
  • 좌표 압축
  • Binary Indexed Tree (펜윅 트리)
    을 함께 사용하는 고급 알고리즘 문제야.
    이걸 정보처리기사 실기 난이도에 맞춰서,
    핵심 개념은 유지하되 코드 길이와 난이도는 대폭 줄인 문제를 새로 구성해줄게.

✅ 목표: 정보처리기사 실기 수준의 문제로 단순화

🧠 사용하는 개념

  • 2차원 평면 → 1차원 배열로 단순화
  • 좌표 압축
  • Binary Indexed Tree (BIT) 적용
  • 이벤트 정렬 생략 (단순 순차 처리로 대체)

💡 문제 설명: 단순화된 EGG 문제

❓문제 이름: "알을 밟은 학생 수"

선생님은 학생들에게 점선으로 줄을 서도록 지시했다.
그 줄에는 N개의 알이 놓여 있으며, 각 알은 [시작 위치, 끝 위치] 구간을 점유한다.
선생님은 학생들에게 M개의 특정 위치를 짚게 하며,
그 위치에 몇 개의 알이 깔려 있는지를 확인하고자 한다.

  • 알은 모두 1차원 선분으로 표현된다.
  • 한 알은 [a, b] 형태로 주어지며, a ≤ b
  • 각 쿼리는 위치 x에 대해, [a, b]x가 포함된 알의 개수를 묻는다.

🔢 입력 형식

N M
a1 b1
a2 b2
...
aN bN
x1
x2
...
xM
  • N: 알의 개수 (1 ≤ N ≤ 1000)
  • M: 쿼리 개수 (1 ≤ M ≤ 1000)

💬 출력 형식

q1
q2
...
qM

각 쿼리에 대해, 해당 위치에 존재하는 알의 개수를 출력


📘 예제 입력

3 4
1 5
3 7
6 9
1
4
6
10

📗 예제 출력

1
2
2
0

✅ 풀이 전략 (간단한 BIT + 좌표 압축)

  1. 모든 좌표를 수집 (a, b, 쿼리 x)
  2. 좌표 압축: 작은 숫자부터 인덱스를 부여 (1번부터)
  3. BIT에 각 알의 시작점에 +1, 끝점+1에 -1을 더함 → 구간 누적 처리
  4. 쿼리는 해당 좌표에 대해 BIT 누적합(query(x)) 계산

✅ 전체 코드 (Python)

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 업데이트 후:

  • update(1, +1)
  • update(6, -1)
  • update(3, +1)
  • update(8, -1)
  • update(6, +1)
  • update(10, -1)

→ 각 쿼리 결과는 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

🔍 각 메서드 디버깅


1️⃣ update(bit, i, v)

def update(bit, i, v):
    while i < len(bit):
        bit[i] += v
        i += i & -i

✅ 역할:

  • BIT 트리에서 위치 i와 그 상위 노드에 v를 더해 누적

✅ 예: update(bit, 1, 1)

  • i = 1
  • bit[1] += 1
  • i = 1 + (1 & -1) = 2
  • bit[2] += 1
  • i = 4, bit[4] += 1
  • i = 8, bit[8] += 1

→ BIT는 구간 누적합을 만들기 위해 관련 노드 모두를 갱신


2️⃣ query(bit, i)

def query(bit, i):
    s = 0
    while i > 0:
        s += bit[i]
        i -= i & -i
    return s

✅ 역할:

  • BIT 배열에서 1~i까지 누적합을 구함

✅ 예: query(bit, 4)

  • s = 0
  • s += bit[4]
  • i = 0 → 종료
    s = 누적된 개수

3️⃣ 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
}

4️⃣ 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)

✅ 역할:

  • 각 알의 [a, b] 구간을 BIT에 누적
  • 시작점에 +1, 끝+1 위치에 -1

✅ 예제에서:

  • (1,5) → update(1, +1), update(6, -1)
  • (3,7) → update(2, +1), update(8, -1)
  • (6,9) → update(5, +1), update(10, -1)

5️⃣ process_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

✅ 역할:

  • 각 쿼리 위치에서, 그 시점에 쌓여 있는 알의 수를 BIT에서 조회

✅ 예제:

  • query(1) → 1개
  • query(4) → 2개
  • query(6) → 2개
  • query(10) → 0개

🔚 최종 결과 출력

for r in results:
    print(r)

출력:

1
2
2
0

📌 마무리 요약

메서드설명예제 처리
updateBIT에서 값 누적알 시작/끝 처리
queryBIT에서 누적 개수 조회쿼리 처리
compress_coordinates좌표 압축110 좌표 → 18로
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

📌 먼저 해야 할 작업: 좌표 압축 결과 보기

수집된 좌표들:

  • 알: 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부터 시작)
11
32
43
54
65
76
87
108

🎯 이제 update(bit, coord_map[a], 1), update(bit, coord_map[b+1], -1) 정리

입력 알들:

  1. [1, 5] → a=1, b=5
  2. [3, 7] → a=3, b=7
  3. [6, 9] → a=6, b=9

1. 알 [1, 5]

  • a = 1coord_map[1] = 1update(bit, 1, 1)
  • b+1 = 6coord_map[6] = 5update(bit, 5, -1)

2. 알 [3, 7]

  • a = 3coord_map[3] = 2update(bit, 2, 1)
  • b+1 = 8coord_map[8] = 7update(bit, 7, -1)

3. 알 [6, 9]

  • a = 6coord_map[6] = 5update(bit, 5, 1)
  • b+1 = 10coord_map[10] = 8update(bit, 8, -1)

✅ 정리된 update 호출 전체

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의 용도 상 이 의미는?

  • bit[i]는 "i번 좌표에서 구간이 얼마나 시작되었는지"를 기록해
  • update() 함수는 이 정보를 트리 형태로 누적
  • query(x)는 "x좌표에서 총 몇 개의 알이 겹치는가"를 빠르게 알게 해줌

✨ 시각적으로 보면

압축된 좌표 인덱스실제 좌표bit에 적용된 변화
11+1 (알1 시작)
23+1 (알2 시작)
56-1 (알1 끝), +1 (알3 시작) → 총 변화: 0
78-1 (알2 끝)
810-1 (알3 끝)

좋아!
이제 update(bit, i, v) 함수에 실제로 앞에서 구한 좌표 압축 인덱스와 값들을 넣고,
각 호출이 BIT 배열을 어떻게 바꾸는지 한 줄씩 디버깅해보자.


🔄 BIT 배열 초기 상태

  • bit 길이는 len(coord_map) + 5
  • len(coord_map) = 8 (압축된 좌표 수)
  • 따라서, bit 길이 대략 13 (0~12 인덱스), 인덱스 0은 사용 안 함
  • 초기 값은 모두 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 호출:

호출 순서함수 호출설명
1update(bit, 1, 1)알1 시작
2update(bit, 5, -1)알1 끝 다음
3update(bit, 2, 1)알2 시작
4update(bit, 7, -1)알2 끝 다음
5update(bit, 5, 1)알3 시작
6update(bit, 8, -1)알3 끝 다음

1️⃣ update(bit, 1, 1)

  • 초기 i = 1, v = 1
while 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]

2️⃣ update(bit, 5, -1)

  • i = 5, v = -1
while 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]

3️⃣ update(bit, 2, 1)

  • i = 2, v = 1
while 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]

4️⃣ update(bit, 7, -1)

  • i = 7, v = -1
while 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]

5️⃣ update(bit, 5, 1)

  • i = 5, v = 1
while 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]

6️⃣ update(bit, 8, -1)

  • i = 8, v = -1
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` 배열 상태

```plaintext
bit = [0, 1, 2, 0, 2, 0, 0, -1, 0, 0, 0, 0, 0]

✅ 최종 결과

인덱스012345678...
0120200-10...

0개의 댓글