이 문제는 구간 업데이트와 구간 쿼리가 빈번하게 발생하는 상황에서 세그먼트 트리(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_4fade9ae6456785d.png)
![[Algorithm] C++ 백준 123336번 A Sorting Problem - 역순쌍 개수](/post/algorithm/2025-12-04-boj-23336-sorting-problem-inversion-count-cpp-solution/wordcloud_hu_2ae546143d7768fd.png)
![[Algorithm] C++ 백준 12844번: XOR](/post/algorithm/2025-12-04-boj-12844-xor/wordcloud_hu_72acdccb71715643.png)
![[Algorithm] C++ 백준 12850번 본대 산책2](/post/algorithm/2025-12-04-boj-12850-campus-walk-matrix-exponentiation-cpp-solution/wordcloud_hu_6f8f9e4e82600159.png)
![[Algorithm] C++ 백준 13182번 제비](/post/algorithm/2025-12-04-boj-13182-lottery-draw-expectation-cpp-solution/wordcloud_hu_6c18b6c0307518c2.png)
![[Algorithm] C++ 백준 16313번: Janitor Troubles](/post/algorithm/2025-12-04-boj-16313-janitor-troubles-geometry-brahmagupta-cpp-solution/wordcloud_hu_9e3b0424f18fce0b.png)
![[Algorithm] C++ 백준 13538번: XOR 쿼리 - 퍼시스턴트 트라이](/post/algorithm/2025-08-14-boj-13538-xor-query-persistent-trie-cpp-solution/wordcloud_hu_672c93ae9f970898.png)
![[Algorithm] C++ 백준 17476번: 수열과 쿼리 28](/post/algorithm/2025-09-04-boj-17476-sequence-and-queries-28-segtree-beats-cpp-solution/wordcloud_hu_99e0dd88606935.png)
![[Algorithm] C++ 백준 17429번: 국제 메시 기구](/post/algorithm/2025-08-14-boj-17429-international-messi-organization-hld-lazy-segtree-cpp-solution/wordcloud_hu_6ebd6ef8983e9f39.png)
![[Algorithm] C++ 백준 17474번 : 수열과 쿼리](/post/algorithm/2025-08-12-boj-17474-sequence-and-queries-26-cpp-solution/wordcloud_hu_6ccae3cf848cbb6c.png)
![[Algorithm] C++/Python 백준 16993번: 연속합과 쿼리 (세그먼트 트리)](/post/algorithm/2025-09-16-boj-16993-maximum-subarray-queries-segment-tree-cpp-python-solution/wordcloud_hu_789f7bcec4a582b7.png)