이 문제는 구간 업데이트와 구간 쿼리가 빈번하게 발생하는 상황에서 세그먼트 트리(Segment Tree)와 느리게 갱신되는 세그먼트 트리(Lazy Propagation) 기법을 사용하여 해결해야 하는 전형적인 문제입니다. 단순 반복문으로는 시간 초과가 발생하므로 $O(\log N)$의 효율적인 처리가 필수적입니다.
문제 정보
문제 링크: https://www.acmicpc.net/problem/12844
문제 요약: 길이가 $N$인 수열 $A$에 대해 두 가지 쿼리를 처리해야 합니다. 첫 번째는 구간 $[i, j]$의 모든 원소에 $k$를 XOR 연산하는 업데이트 쿼리이고, 두 번째는 구간 $[i, j]$의 모든 원소를 XOR한 값을 출력하는 쿼리입니다.
제한 조건:
- 시간 제한: 2초
- 메모리 제한: 512MB
- 입력 크기: $1 \le N, M \le 500,000$, $0 \le A_i, k \le 100,000$
입출력 예제
입력 1:
| |
출력 1:
| |
접근 방식
핵심 관찰
XOR 연산은 교환법칙과 결합법칙이 성립하며, 자기 자신과의 XOR은 0이 되는 성질($x \oplus x = 0$)을 가집니다. 구간 $[L, R]$에 값 $k$를 XOR할 때, 구간의 길이가 짝수라면 $k$가 짝수 번 XOR되므로 전체 XOR 합은 변하지 않습니다. 구간의 길이가 홀수라면 $k$가 홀수 번 XOR되므로 전체 XOR 합에 $k$를 한 번 XOR한 것과 같습니다. 이 성질을 이용하여 Lazy Propagation을 구현할 수 있습니다.
알고리즘 설계 (Mermaid Flowchart)
flowchart TD
A[시작: N, 배열 입력] --> B[세그먼트 트리 초기화]
B --> C[M개의 쿼리 처리 루프]
C --> D{쿼리 타입 확인}
D -- 타입 1 --> E[Update Range: Lazy 적용]
D -- 타입 2 --> F[Query Range: Lazy 적용]
E --> G{구간 겹침 확인}
G -- 일부 겹침 --> H[자식 노드로 분할 재귀]
G -- 완전 포함 --> I[Lazy 값 갱신 및 Tree 값 갱신]
F --> J{구간 겹침 확인}
J -- 일부 겹침 --> K[자식 노드 결과 XOR 합]
J -- 완전 포함 --> L[현재 노드 값 반환]
H --> M[자식 결과 합침]
K --> N[결과 반환]
I --> O[다음 쿼리]
L --> P[출력]
P --> O
M --> O
N --> P
단계별 로직
- 전처리: 입력 배열을 리프 노드로 하는 세그먼트 트리를
init함수를 통해 구성합니다. 각 노드는 해당 구간의 XOR 합을 저장합니다. - 메인 로직:
- Lazy Propagation:
update_lazy함수를 통해 노드에 쌓인lazy값을 자식에게 전파하고 현재 노드의 값을 갱신합니다. 이때 구간의 길이가 홀수인 경우에만tree값에lazy값을 XOR합니다. - 구간 업데이트:
update_range함수에서 구간에 완전히 포함되는 노드를 만나면lazy값을 갱신하고 즉시 리턴합니다. 그렇지 않으면 자식 노드로 내려갑니다. - 구간 쿼리:
query함수에서 구간에 완전히 포함되는 노드의 값을 반환하고, 걸쳐있는 경우 자식 노드들의 XOR 합을 반환합니다.
- Lazy Propagation:
- 후처리: 쿼리 2번의 수행 결과를 표준 출력으로 반환합니다.
복잡도 분석
| 항목 | 복잡도 | 비고 |
|---|---|---|
| 시간 복잡도 | $O(M \log N)$ | 트리 구성 $O(N)$, 각 쿼리 $O(\log N)$ |
| 공간 복잡도 | $O(N)$ | 트리 및 Lazy 배열 크기 $4N$ |
구현 코드
C++
| |
코너 케이스 및 실수 포인트
| 케이스 | 설명 | 처리 방법 |
|---|---|---|
| i > j 입력 | 쿼리 구간의 시작과 끝이 뒤집혀서 들어오는 경우 | swap(a, b)를 통해 항상 $a \le b$가 되도록 조정 |
| 구간 길이 홀/짝 | XOR 연산은 구간 길이에 따라 결과가 달라짐 | 홀수일 때만 lazy 값을 tree에 XOR 적용 |
| Lazy 전파 시점 | 업데이트나 쿼리 방문 시 반드시 lazy 갱신 | 함수 진입 초기에 update_lazy 호출 |
| N 범위 | $N$이 최대 50만이므로 트리 크기는 $4N$ 필요 | 벡터 크기를 충분히 할당 (resize(n * 4)) |
마무리
이 문제는 Lazy Propagation의 개념을 이해하고 XOR 연산의 특성을 잘 활용해야 하는 문제입니다. 구간 합 문제와 유사하지만 XOR의 성질($A \oplus A = 0$)로 인해 구간 길이에 따른 처리가 중요하다는 점이 다릅니다.
![Featured image of post [Algorithm] C++ 백준 12844번: XOR](/post/algorithm/2025-12-04-boj-12844-xor/wordcloud_hu_68f523195a710e93.webp)
![[Algorithm] C++ 백준 19693번: Safety](/post/algorithm/2025-12-08-boj-19693-safety-stacks-smooth-cpp-solution/wordcloud_hu_70f209370c7ab557.webp)
![[Algorithm] C++ 백준 2709번: 가장 작은 K](/post/algorithm/2025-12-08-boj-2709-smallest-k-last-digits-1-2-cpp-solution/wordcloud_hu_9737a176fd48c4c9.webp)
![[Algorithm] C++ 백준 12844번: XOR](/post/algorithm/2025-12-04-boj-12844-xor/wordcloud_hu_344941b860739c59.webp)
![[Algorithm] C++ 백준 12850번 본대 산책2](/post/algorithm/2025-12-04-boj-12850-campus-walk-matrix-exponentiation-cpp-solution/wordcloud_hu_8602c2af7cc44fc0.webp)
![[Algorithm] C++ 백준 13182번 제비](/post/algorithm/2025-12-04-boj-13182-lottery-draw-expectation-cpp-solution/wordcloud_hu_1623058451cb449c.webp)
![[Algorithm] C++ 백준 14899번: 수열과 쿼리 19](/post/algorithm/2025-12-19-boj-14899-sequence-and-queries-19-segtree-beats-cpp-solution/wordcloud_hu_236efca540bc042b.webp)
![[Algorithm] C++ 백준 24271번: xor²](/post/algorithm/2026-01-24-boj-24271-xor-squared-xor-permutation-segment-tree-cpp-solution/wordcloud_hu_b3bbd659bfcf0558.webp)
![[Algorithm] C++ 백준 15782번: Calculate! 2](/post/algorithm/2025-12-02-boj-15782-calculate-2-tree-segtree-lazy-cpp-solution/wordcloud_hu_e90101ea3a15a18d.webp)
![[Algorithm] C++ 백준 17474번 : 수열과 쿼리](/post/algorithm/2025-08-12-boj-17474-sequence-and-queries-26-cpp-solution/wordcloud_hu_51b1ef37a61e4919.webp)
![[Algorithm] C++ 백준 17408번: 수열과 쿼리 24](/post/algorithm/2026-02-23-boj-17408-sequence-and-query-24-segment-tree-cpp-solution/wordcloud_hu_af2d4d2a8fdc290e.webp)