|
| 1 | +# Authored by : leejh9226 |
| 2 | +# Co-authored by : - |
| 3 | +# Link : http://boj.kr/171a010d7b2247588d9dba8b3fa192c2 |
| 4 | + |
| 5 | +import heapq |
| 6 | +import sys |
| 7 | + |
| 8 | +def input(): |
| 9 | + return sys.stdin.readline().rstrip() |
| 10 | + |
| 11 | + |
| 12 | +max_g_heap = [[] for _ in range(101)] |
| 13 | +min_g_heap = [[] for _ in range(101)] |
| 14 | +max_l_heap = [[] for _ in range(101)] |
| 15 | +min_l_heap = [[] for _ in range(101)] |
| 16 | + |
| 17 | +l_lookup = [0] * 100001 |
| 18 | +g_lookup = [0] * 100001 |
| 19 | + |
| 20 | +removed = set() |
| 21 | + |
| 22 | + |
| 23 | +def add(p, l, g): |
| 24 | + """ |
| 25 | + recommend에서 사용할 수 있도록 알고리즘 유형, 난이도별로 생성해둔 최소, 최대힙에 문제를 추가 |
| 26 | +
|
| 27 | + Q. 삭제된 적이 있지만 아직 heap에 해당 문제가 남아있을 수 있지 않나요? |
| 28 | + A. heap에는 삭제된 문제가 남아있을 수 있어, 동일한 문제를 추가하면 해당 heap에는 똑같은 문제 2개가 존재하게 됨. 하지만 이후 해당 |
| 29 | + 문제를 다시한번 삭제한 다음 조회하는 경우 두 문제 모두 efficient_top_g 함수에서 제거되기 때문에 문제가 발생하지 않음. 문제를 다시 |
| 30 | + 삭제하기 이전이라면, 둘 중 어떤 문제(인스턴스)가 조회되더라도 동일한 답이 나오기 때문에 문제가 발생하지 않음. |
| 31 | + """ |
| 32 | + |
| 33 | + # 각각 100001, 101보다 큰 2진수 값(2**17, 2**7)을 곱하여 integer값 하나에 3가지 정보를 담음. 이 값을 lpg라고 명명 |
| 34 | + lpg = l << 24 | p << 7 | g |
| 35 | + |
| 36 | + # 동일한 난이도, 문제번호, 알고리즘 유형의 문제가 이미 삭제된 적이 있다면, 삭제된 기록을 제거 |
| 37 | + if is_removed(lpg): |
| 38 | + removed.remove(lpg) |
| 39 | + |
| 40 | + # 난이도 > 문제번호 > 알고리즘 유형 우선순위로 정렬(최소, 최대힙) |
| 41 | + heapq.heappush(max_g_heap[G], -lpg) |
| 42 | + heapq.heappush(min_g_heap[G], lpg) |
| 43 | + heapq.heappush(max_l_heap[L], -lpg) |
| 44 | + heapq.heappush(min_l_heap[L], lpg) |
| 45 | + # solved에서 사용할 수 있도록 문제번호-난이도, 문제번호-알고리즘 유형 매핑 정보를 저장 |
| 46 | + l_lookup[P] = l |
| 47 | + g_lookup[P] = g |
| 48 | + |
| 49 | + |
| 50 | +def remove(p, l, g): |
| 51 | + """ |
| 52 | + 난이도, 문제번호, 알고리즘 유형으로 식별되는 유일한 문제를 "삭제되었음"으로 표시 |
| 53 | + - 실제 힙에서 삭제하는 동작은, recommend에서 heappop을 호출할 때 "삭제되었음"으로 표시된 문제를 제거하는 것 |
| 54 | + """ |
| 55 | + removed.add(l << 24 | p << 7 | g) |
| 56 | + |
| 57 | + |
| 58 | +def is_removed(lpg): |
| 59 | + """ |
| 60 | + 난이도, 문제번호, 알고리즘 유형으로 식별되는 유일한 문제가 제거되었는지 확인 |
| 61 | + max_heap에서는 음수 값으로 저장되어 있으므로, 절대값을 취한 뒤 비교 |
| 62 | + """ |
| 63 | + if abs(lpg) in removed: |
| 64 | + return True |
| 65 | + return False |
| 66 | + |
| 67 | + |
| 68 | +def efficient_top(target_heap): |
| 69 | + """ |
| 70 | + 최상위 문제가 이미 제거된 문제라면, 최상위 문제를 제거하고 다음 최상위 문제를 찾음 |
| 71 | + 삭제되지 않고, 조건을 만족하는 문제가 존재한다면, 문제번호를 반환 |
| 72 | + """ |
| 73 | + while target_heap and is_removed(target_heap[0]): |
| 74 | + heapq.heappop(target_heap) |
| 75 | + |
| 76 | + if target_heap: |
| 77 | + return (abs(target_heap[0]) >> 7) & 0x1FFFF |
| 78 | + return None |
| 79 | + |
| 80 | + |
| 81 | +n = int(input()) |
| 82 | +for _ in range(n): |
| 83 | + P, L, G = map(int, input().split()) |
| 84 | + add(P, L, G) |
| 85 | + |
| 86 | +q = int(input()) |
| 87 | +for _ in range(q): |
| 88 | + cmd = input().split() |
| 89 | + |
| 90 | + # case 분기 |
| 91 | + match cmd[0]: |
| 92 | + case "recommend": |
| 93 | + """ |
| 94 | + recommend G x |
| 95 | + x가 1인경우 -> 알고리즘 분류가 G인 문제중 가장 어려운것 출력 |
| 96 | + 조건을 만족하는 문제가 많다면 문제번호가 큰것 |
| 97 | + x가 -1인경우 -> 알고리즘 분류가 G인 문제중 가장 쉬운것 출력 |
| 98 | + 조건을 만족하는 문제가 많다면 문제번호가 작은것 |
| 99 | + |
| 100 | + 알고리즘 분류 G마다 각각의 최대, 최소힙을 사용하여 문제를 저장한다. |
| 101 | + - 힙 정렬 기준은 난이도 > 문제번호 > 알고리즘 분류 순서대로 |
| 102 | + - 알고리즘 분류를 포함한 이유는, 힙에서 꺼낸 문제를 유일하게 식별하여 이미 풀린 문제인지 확인하기 위함 |
| 103 | + """ |
| 104 | + G, x = int(cmd[1]), int(cmd[2]) |
| 105 | + if x == 1: |
| 106 | + print(efficient_top(max_g_heap[G])) |
| 107 | + else: |
| 108 | + print(efficient_top(min_g_heap[G])) |
| 109 | + case "recommend2": |
| 110 | + """ |
| 111 | + recommend2 x |
| 112 | + x가 1인경우 -> 가장 어려운 문제 출력 |
| 113 | + 조건을 만족하는 문제가 많다면 문제번호가 큰것 |
| 114 | + x가 -1인경우 -> 가장 쉬운 문제 출력 |
| 115 | + 조건을 만족하는 문제가 많다면 문제번호가 작은것 |
| 116 | + |
| 117 | + 난이도 L마다 각각의 최대, 최소힙을 사용하여 문제를 저장한다. |
| 118 | + - 힙 정렬 기준은 난이도 > 문제번호 > 알고리즘 분류 순서대로 |
| 119 | + - 난이도를 포함한 이유는, 힙에서 꺼낸 문제를 유일하게 식별하여 이미 풀린 문제인지 확인하기 위함 |
| 120 | + - x == 1인 경우 -> 난이도 100부터 1까지 순회하며, 해당 난이도에서 문제번호가 가장 큰 문제를 출력 |
| 121 | + - x == -1인 경우 -> 난이도 1부터 100까지 순회하며, 해당 난이도에서 문제번호가 가장 작은 문제를 출력 |
| 122 | + """ |
| 123 | + x = int(cmd[1]) |
| 124 | + if x == 1: |
| 125 | + for i in range(100, 0, -1): |
| 126 | + if (a := efficient_top(max_l_heap[i])) is not None: |
| 127 | + print(a) |
| 128 | + break |
| 129 | + else: |
| 130 | + for i in range(1, 101, 1): |
| 131 | + if (a := efficient_top(min_l_heap[i])) is not None: |
| 132 | + print(a) |
| 133 | + break |
| 134 | + case "recommend3": |
| 135 | + """ |
| 136 | + recommend3 x L |
| 137 | + x가 1인경우 -> 난이도가 L이상인 문제 중 가장 쉬운것 출력 |
| 138 | + 조건을 만족하는 문제가 많다면 문제번호가 작은것 |
| 139 | + 없다면 -1 출력 |
| 140 | + x가 -1인경우 -> 난이도가 L미만인 문제 중 가장 어려운것 출력 |
| 141 | + 조건을 만족하는 문제가 많다면 문제번호가 큰것 |
| 142 | + 없다면 -1 출력 |
| 143 | + |
| 144 | + 난이도 L마다 각각의 최대, 최소힙을 사용하여 문제를 저장한다. |
| 145 | + - 힙 정렬 기준은 난이도 > 문제번호 > 알고리즘 분류 순서대로 |
| 146 | + - 난이도를 포함한 이유는, 힙에서 꺼낸 문제를 유일하게 식별하여 이미 풀린 문제인지 확인하기 위함 |
| 147 | + - x == 1인 경우 -> 난이도 L부터 100까지 순회하며, 해당 난이도에서 문제번호가 가장 작은 문제를 출력 |
| 148 | + - x == -1인 경우 -> 난이도 L-1부터 1까지 순회하며, 해당 난이도에서 문제번호가 가장 큰 문제를 출력 |
| 149 | + """ |
| 150 | + x, L = int(cmd[1]), int(cmd[2]) |
| 151 | + if x == 1: |
| 152 | + for i in range(L, 101): |
| 153 | + if (a := efficient_top(min_l_heap[i])) is not None: |
| 154 | + print(a) |
| 155 | + break |
| 156 | + else: |
| 157 | + print(-1) |
| 158 | + else: |
| 159 | + for i in range(L - 1, 0, -1): |
| 160 | + if (a := efficient_top(max_l_heap[i])) is not None: |
| 161 | + print(a) |
| 162 | + break |
| 163 | + else: |
| 164 | + print(-1) |
| 165 | + case "add": |
| 166 | + P, L, G = int(cmd[1]), int(cmd[2]), int(cmd[3]) |
| 167 | + add(P, L, G) |
| 168 | + case _: # "solved" |
| 169 | + P = int(cmd[1]) |
| 170 | + remove(P, l_lookup[P], g_lookup[P]) |
| 171 | + |
| 172 | +""" Solution Description |
| 173 | +알고리즘 유형, 난이도별로 최대, 최소힙을 사용하여 문제를 저장하고, 힙에서 문제를 꺼낼 때 이미 삭제된 문제인지 확인한 다음, 조건에 |
| 174 | +맞게 문제 번호를 출력했습니다. |
| 175 | +""" |
0 commit comments