본문으로 바로가기
제품데스크톱 도구TASTECAD도면 확인TASTEFILES파일 관리TASTEFTP파일 전송TASTESSH원격 작업공간TASTEZIP압축과 해제개발 도구TASTECODERust 통합 개발 환경TASTESTUDIO통합 개발·테스트 환경TASTEETL데이터 통합(ETL)서버TASTEREMOTE원격 데스크톱 서버TASTEGEO공간정보 서버TASTEMAIL메일 작업공간시작하기다운로드문서구매모든 제품
마켓플레이스
소식
개발소식오늘의 개발 소식개발 문서프로그래밍 언어RustTypeScriptJavaScriptPythonGoC#JavaCC++KotlinDart프런트엔드 프레임워크Next.jsReactVue.js · NuxtSvelteKitAngular서버 프레임워크Spring BootDjangoFastAPIASP.NET CoreNestJS · Express앱 · 데스크톱 프레임워크TauriFlutterReact NativeElectron알고리즘알고리즘 기초알고리즘 핵심알고리즘 심화실무 알고리즘
커뮤니티프로젝트개발 로그프롬프트AI 도구 이슈쇼케이스커뮤니티 안내
협업기술 전수홈페이지 구축데스크톱 앱 구축릴리스·결제 파이프라인판매 파트너문의
서비스원격 데스크톱설치 패키지 커스텀
TASTEDEV
제품

데스크톱 도구

TASTECAD복잡한 도면을 가볍게 열다TASTEFILES필요한 파일만, 가장 빠르게TASTEFTP파일이 오가는 가장 단정한 길TASTESSH서버와 나 사이, 더 단단한 연결TASTEZIP더 빠르게 묶고, 더 안전하게 풀다

개발 도구

TASTECODERust 앱을 편집 · 빌드 · 디버깅 · 테스트까지 한 창에서TASTESTUDIO편집부터 원격 테스트와 승인 기반 AI 수정까지TASTEETL값을 보며 데이터를 옮깁니다

서버

TASTEREMOTE우리 회사 서버로 운영하는 원격 데스크톱TASTEGEOGeoServer 호환 공간정보 서버를 Rust로 다시 세웁니다TASTEMAIL메일의 주도권을 다시 내 손에

시작하기

다운로드모든 제품 최신 판문서제품 사용법구매구독 · 영구 라이선스모든 제품모든 제품 한눈에
모든 제품
마켓플레이스소식
개발

소식

오늘의 개발 소식언어 · 프레임워크 · GitHub · AI 공식 발표개발 문서언어 · 프레임워크 · 알고리즘 안내서

프로그래밍 언어

RustTypeScriptJavaScriptPythonGoC#JavaCC++KotlinDart

프레임워크

프런트엔드 프레임워크Next.jsReactVue.js · NuxtSvelteKitAngular
서버 프레임워크Spring BootDjangoFastAPIASP.NET CoreNestJS · Express
앱 · 데스크톱 프레임워크TauriFlutterReact NativeElectron

알고리즘

알고리즘 기초복잡도 분석 · 정렬 · 탐색 · 기본 자료구조 · 트리와 힙알고리즘 핵심그래프 탐색 · 최단 경로와 최소 신장 트리 · 동적 계획법 · 탐욕 알고리즘 · 분할 정복 · 백트래킹알고리즘 심화문자열 알고리즘 · 수학과 정수론 · 비트 연산 · 구간 트리 · 유니온 파인드실무 알고리즘코딩 면접 풀이 전략 · LRU 캐시 · 레이트 리미터 · 일관된 해시
개발 문서 전체 보기
커뮤니티
프로젝트빌더와 작업 발견개발 로그결정과 시행착오프롬프트재사용 가능한 지시AI 도구 이슈Claude · Codex · Gemini 문제와 우회법쇼케이스완성된 결과커뮤니티 안내공개하고 함께 만드는 방식
협업
기술 전수Rust·egui 네이티브 앱 만들기홈페이지 구축10개 언어, 계정, 문서·다운로드데스크톱 앱 구축Rust·egui, WebView 없이릴리스·결제 파이프라인서명 · MSI·deb·rpm·AppImage · 해시판매 파트너소개하고 결제액의 30% 받기문의구축 · 제휴 · 투자 문의
서비스
원격 데스크톱브라우저로 내 컴퓨터에 접속설치 패키지 커스텀필요한 기능만, 설정은 미리
구매
KO
한국어EnglishDeutschEspañolFrançaisItalianoPortuguês (Brasil)日本語简体中文繁體中文
로그인
로그인
TASTEDEV

작은 도구부터, 더 좋은 개발까지.

support@tastedev.netTASTEDEV 후원하기
제품제품군구매문서다운로드마켓플레이스고객 지원
커뮤니티프로젝트개발 로그프롬프트AI 도구 이슈쇼케이스
정책개인정보처리방침이용약관구독 약관환불 정책커뮤니티 운영 정책
계정로그인가입하기
방문자
오늘
012345678901234567890123456789012345678901234567890123456789
어제
012345678901234567890123456789012345678901234567890123456789
누적 조회
0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789
상호
지엑스소프트
대표자
최영구
사업자등록번호
853-44-00368
주소
인천광역시 미추홀구 경인로 351 9층 V24
전화
+82-10-4404-1523
이메일
whitecalvin@tastedev.net
개인정보 보호책임자
최영구

© 2026 TASTEDEV제품 정보는 현재 검증된 개발 소스를 기준으로 합니다.출시·고도화 중

구간 트리 문서
구간 트리
개념동작 원리구현복잡도연습 문제실무 활용
토론

구간 트리 안내서 · 5/6

연습 문제

이 장의 문제는 구간 트리와 펜윅 트리의 대표 유형을 연습하도록 새로 만든 것입니다. 먼저 혼자 풀어 본 뒤 접근 방법과 풀이를 확인하세요. 모든 풀이는 표준 라이브러리만 쓰는 Python 코드입니다.

문제 1. 일별 판매량 장부

어떤 매장의 n일치 판매량이 배열로 주어집니다. 두 종류의 요청을 차례로 처리합니다. ("set", d, v)는 d일의 판매량을 v로 고치고, ("sum", l, r)은 l일부터 r-1일까지의 판매량 합을 출력합니다. n과 요청 수는 각각 최대 200,000입니다.

접근: 질의가 구간 합이므로 펜윅 트리가 가장 간단합니다. 펜윅 트리는 더하기만 받으므로 원래 배열을 따로 들고 있다가 차이를 더합니다. 처음 만들 때 add를 n번 부르면 O(n log n)이지만, 아래처럼 자기 값을 바로 위 부모에게 넘기면 O(n)에 만들 수 있습니다.

def solve(sales, requests):
    n = len(sales)
    tree = [0] + sales[:]
    for i in range(1, n + 1):              # O(n) 만들기
        parent = i + (i & -i)
        if parent <= n:
            tree[parent] += tree[i]

    def add(i, delta):
        i += 1
        while i <= n:
            tree[i] += delta
            i += i & -i

    def prefix(count):
        total = 0
        while count > 0:
            total += tree[count]
            count -= count & -count
        return total

    out = []
    for kind, x, y in requests:
        if kind == "set":
            add(x, y - sales[x])
            sales[x] = y
        else:
            out.append(prefix(y) - prefix(x))
    return out


print(solve([4, 0, 7, 2, 5], [("sum", 1, 4), ("set", 1, 6), ("sum", 0, 5)]))   # [9, 24]

문제 2. 가장 추운 구간

센서 n개의 온도가 주어집니다. ("set", i, t)는 센서 i의 온도를 t로 바꾸고, ("min", l, r)은 센서 l부터 r-1까지 가운데 가장 낮은 온도를 답합니다.

접근: 최솟값은 빼기로 되돌릴 수 없으므로 펜윅 트리로는 풀기 어렵습니다. 합치는 연산을 min, 항등원을 무한대로 둔 구간 트리를 씁니다.

def coldest(temps, requests):
    n = len(temps)
    INF = float("inf")
    tree = [INF] * n + temps
    for i in range(n - 1, 0, -1):
        tree[i] = min(tree[2 * i], tree[2 * i + 1])

    out = []
    for kind, x, y in requests:
        if kind == "set":
            i = x + n
            tree[i] = y
            while i > 1:
                i //= 2
                tree[i] = min(tree[2 * i], tree[2 * i + 1])
        else:
            lo, hi, best = x + n, y + n, INF
            while lo < hi:
                if lo & 1:
                    best = min(best, tree[lo]); lo += 1
                if hi & 1:
                    hi -= 1; best = min(best, tree[hi])
                lo //= 2; hi //= 2
            out.append(best)
    return out


print(coldest([3, -2, 5, 0, 1], [("min", 2, 5), ("set", 3, 4), ("min", 2, 5), ("min", 0, 5)]))
# [0, 1, -2]

문제 3. 순서가 뒤바뀐 쌍 세기

정수 배열에서 i < j이면서 a[i] > a[j]인 쌍(역순 쌍)의 개수를 구합니다. 길이는 최대 200,000이고 값의 범위는 매우 넓습니다.

접근: 왼쪽에서 오른쪽으로 훑으면서, 지금까지 본 값 가운데 현재 값보다 큰 값이 몇 개인지 세어 더합니다. 값의 범위가 넓으므로 먼저 정렬된 순위로 바꾸는 좌표 압축을 하고, 순위별 개수를 펜윅 트리에 담습니다. 전체 O(n log n)입니다.

def count_inversions(a):
    ranks = {v: i + 1 for i, v in enumerate(sorted(set(a)))}
    m = len(ranks)
    tree = [0] * (m + 1)
    seen = inversions = 0
    for v in a:
        r = ranks[v]
        smaller_or_equal, i = 0, r
        while i > 0:
            smaller_or_equal += tree[i]
            i -= i & -i
        inversions += seen - smaller_or_equal
        while r <= m:
            tree[r] += 1
            r += r & -r
        seen += 1
    return inversions


print(count_inversions([8, 4, 2, 1]))         # 6
print(count_inversions([3, 1, 2, 3, 1]))      # 5

문제 4. 구간 할인과 매출 합계

상품 n개의 가격이 있습니다. ("add", l, r, v)는 l번부터 r-1번 상품의 가격에 모두 v를 더하고(할인이면 음수), ("sum", l, r)은 그 구간의 가격 합을 답합니다.

접근: 구간 갱신과 구간 질의가 모두 있으므로 지연 전파 구간 트리를 씁니다. 구간을 완전히 덮는 노드에서는 합에 v * 길이를 더하고 lazy에 v를 쌓아 둔 뒤 멈춥니다. 자식으로 내려가야 할 때만 push로 lazy를 넘깁니다.

def solve_lazy(prices, requests):
    n = len(prices)
    tree, lazy = [0] * (4 * n), [0] * (4 * n)

    def build(node, lo, hi):
        if hi - lo == 1:
            tree[node] = prices[lo]
            return
        mid = (lo + hi) // 2
        build(2 * node, lo, mid); build(2 * node + 1, mid, hi)
        tree[node] = tree[2 * node] + tree[2 * node + 1]

    def apply(node, lo, hi, v):
        tree[node] += v * (hi - lo)
        lazy[node] += v

    def push(node, lo, hi):
        if lazy[node]:
            mid = (lo + hi) // 2
            apply(2 * node, lo, mid, lazy[node])
            apply(2 * node + 1, mid, hi, lazy[node])
            lazy[node] = 0

    def add(node, lo, hi, l, r, v):
        if r <= lo or hi <= l:
            return
        if l <= lo and hi <= r:
            apply(node, lo, hi, v)
            return
        push(node, lo, hi)
        mid = (lo + hi) // 2
        add(2 * node, lo, mid, l, r, v); add(2 * node + 1, mid, hi, l, r, v)
        tree[node] = tree[2 * node] + tree[2 * node + 1]

    def total(node, lo, hi, l, r):
        if r <= lo or hi <= l:
            return 0
        if l <= lo and hi <= r:
            return tree[node]
        push(node, lo, hi)
        mid = (lo + hi) // 2
        return total(2 * node, lo, mid, l, r) + total(2 * node + 1, mid, hi, l, r)

    build(1, 0, n)
    out = []
    for req in requests:
        if req[0] == "add":
            add(1, 0, n, req[1], req[2], req[3])
        else:
            out.append(total(1, 0, n, req[1], req[2]))
    return out


print(solve_lazy([10, 20, 30, 40, 50], [("add", 1, 4, -5), ("sum", 0, 3), ("sum", 2, 5)]))
# [50, 110]

정리

구간 합에 점 갱신이면 펜윅 트리, 최솟값 · 최댓값이면 구간 트리, 구간 갱신까지 섞이면 지연 전파 구간 트리를 먼저 떠올립니다. "앞에서 본 값 중 몇 개가 더 큰가"처럼 개수를 세는 문제는 좌표 압축과 펜윅 트리의 조합으로 자주 풀립니다.

이 장에 대해 질문이 있나요?
이전 장복잡도다음 장실무 활용

댓글 0개

인기 댓글순최신순

로그인 · 로그인하면 댓글을 남길 수 있습니다.