① Node 클래스
class Node:
def __init__(self, value):
self.value = value
self.children = {}
self.isEnd = False # 단어의 끝 표시
② Trie 클래스
class Trie:
def __init__(self):
self.root = Node('') # 공백
def insert(self, str):
node = self.root # 루트에서 시작
for char in str: # 삽입할 문자열의 각 문자
if char not in node.children: # root 노드의 children에 첫 문자가 없으면
newNode = Node(char) # 첫 문자를 갖는 새로운 노드 생성
node.children[char] = newNode # 생성된 노드를 root 노드의 자식 노드로 추가
node = newNode # 자식 노드로 이동
else:
node = node.children[char]
node.isEnd = True # 단어의 끝 표시
def search(self, str):
node = self.root # 루트에서 시작
for char in str: # 탐색하려는 문자열의 각 문자
if char in node.children: # root 노드의 children에 첫 문자가 있는 경우
node = node.children[char] # 자식 노드로 이동
else: # root 노드의 children에 첫 문자가 없는 경우
return False
return node.isEnd # 문자열의 끝에 도달했을 때, 단어도 끝나는지 확인
③ 트라이의 단서
④ 입력 값의 개수를 알 수 없는 경우
while True:
try:
N = int(input())
except:
break
2 ^ k ≥ N
을 만족하는 k의 최소 값에 대해 2 ^ k * 2
를 계산한 수치가 된다.2 ^ k
부터 차례대로 데이터를 삽입한다.2i
, 오른쪽 자식은 2i + 1
인 것을 이용하여 구간 합, 최대 값, 최소 값 중 알아내고자 하는 값을 계산한다.length = N
height = 0 # k 값
while length > 0: # 2 ^ k ≥ N을 만족하는 k의 최소 값을 구하는 과정
length //= 2
height += 1
treeSize = 2 ** (height + 1) # 트리 배열의 사이즈(= 2 ^ k * 2)
leafStartIdx = 2 ** height # 리프 노드의 시작 인덱스
tree = [0] * (treeSize)
# 데이터를 리프 노드에 저장
for i in range(leafStartIdx, leafStartIdx + N): # 기존 배열의 원소 수가 리프 노드 수보다 작거나 같으므로, treeSize를 끝 범위로 설정하면 안됨.
tree[i] = int(input())
# 나머지 노드에 값 할당
def setTree(i):
while i > 1: # 루트 노드까지만 구하면 됨.
tree[i // 2] += tree[i] # 자식 노드의 인덱스가 2i, 2i + 1인 것을 이용하여 구간 합 계산
i -= 1
setTree(treeSize - 1) # 높은 인덱스에서 낮은 인덱스 순서로 노드에 값을 할당
① 구간 합
② 최대/최소 값
# 데이터 업데이트 함수
def updateValue(index, value):
diff = value - tree[index] # 기존 데이터와 새로운 데이터 간의 차이
while index > 0: # 루트 노드에 이르기까지 모든 부모(조상) 노드의 값에 diff만큼을 더함
tree[index] = tree[index] + diff
index //= 2
① Query Range를 트리 배열에 알맞게 변경한다.
2 ^ k - 1
만큼 평행 이동하여, 세그먼트 트리의 range로 변경할 수 있다.② Query 값 구하기
# 구간 합 계산 함수
def getIntervalSum(start, end):
intervalSum = 0
while start <= end:
if start % 2 == 1: # start의 인덱스가 홀수이면, 해당 노드의 값을 intervalSum에 포함
intervalSum += tree[start]
if end % 2 == 0: # end의 인덱스가 짝수이면, 해당 노드의 값을 intervalSum에 포함
intervalSum += tree[end]
# 부모 노드로 이동
start = (start + 1) // 2
end = (end - 1) // 2
return intervalSum
for i in range(M):
query, start, end = map(int, input().split())
if query == 1: # start번째 값을 end로 업데이트
updateValue(start + leafStartIdx -1, end) # 기존 배열의 range를 세그먼트 트리의 range로 변경하하기 위해 2 ^ k - 1만큼 평행 이동
elif query == 2: # start부터 end까지의 구간 합
start = start + leafStartIdx -1
end= end + leafStartIdx -1
print(getIntervalSum(start, end))
① 배열 초기화
② 구간 곱의 데이터 업데이트
def updateValue(index, value):
tree[index] = value
index //= 2
while index > 0:
tree[index] = (tree[index * 2] * tree[index * 2 + 1])
index //= 2
① 일반 LCA
② 개선된 LCA
P[K][N] = P[K-1][P[K-1][N]]
점화식을 이용하여, 각 노드의 2 ^ K(= 0, 1, 2, ...)번째 부모 노드를 배열에 저장한다.트리의 깊이 > 2 ^ K
를 만족하는 최대 값으로 결정된다. 2 ^ K < D
를 만족하는 K의 최대 값을 이용해, 깊이가 같아질 때까지 2 ^ K번 째 부모 노드로 이동한다. 트리의 깊이는 최악의 경우 노드의 개수(N)와 같으므로, 일반 LCA의 최대 시간 복잡도는 O(N)이다. 질의의 개수가 M일 때 시간 복잡도는 O(NM)이 되므로, 이 값이 시간 제약을 초과할 경우에는 개선된 LCA를 사용해야 한다.