문제 : https://www.acmicpc.net/problem/17033
[ 문제 요약 ]
- 노드와 질의 가 주어지며, 각 노드에 대해 초깃값이 주어집니다.
- 1 i v 입력 : i 노드의 값을 v로 변경
- 2 i j 입력 : i 노드부터 j 노드까지 경로의 모든 값들을 XOR 하여 출력
[ 테스트 케이스 설명 ]
5 5 // 노드개수N(2<=100,000), 질의 수Q(1<=100,000)
1 2 4 8 16 // 각 노드의 초기값 0<=1,000,000,000
1 2 // 노드의 연결 관계
1 3
3 4
3 5
2 1 5 // 2인 경우, 1과 5노드 경로사이의 XOR값을 출력하라
1 1 16 // 1인 경우, 1번노드를 16값으로 업데이트하라는 뜻
2 3 5
2 1 5
2 1 3
답
21
20
4
20
[ 문제 해설 ]
- 이 문제를 풀기 위해서는 HLD 알고리즘을 사용해야 합니다. HLD 알고리즘 관련 설명은 아래 링크의 [ 문제 해설 ]에 설명되어 있으니 참고 부탁드립니다.
- https://kyjdummy.tistory.com/21
- HLD 알고리즘을 사용해 트리를 세그먼트 트리에서 사용할 수 있도록 체인으로 분할 후 각 범위에 맞게 XOR 값을 출력해 주면 되는 전형적인 HLD 문제입니다.
[ 정답 코드 ]
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.StringTokenizer;
class Main{
static int N, Q;
static int idx; // 일반 노드 번호를 세그먼트 트리의 인덱스로 변경하기 위한 변수
static int[] value; // 초기에 입력되는 값을 입력받을 배열
static int[] treeIdx; // 노드번호 -> 세그먼트 트리 인덱스 치환
static int[] tree; // 세그먼트 트리
static int[] chainHead; // 해당 체인의 Head값을 담을 배열
static int[] chainLevel; // 해당 체인의 Level을 담을 배열
static int[] chainParent; // 해당 체인에서 다음 체인으로 점프할 노드번호
static ArrayList<Integer>[] adNode;
public static void main(String[] args)throws Exception{
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine());
StringBuilder sb = new StringBuilder();
N = Integer.parseInt(st.nextToken());// 노드개수N(2<=100,000)
Q = Integer.parseInt(st.nextToken());// 질의 수Q(1<=100,000)
value = new int[N + 1];
treeIdx = new int[N + 1];
tree = new int[N * 4];
chainHead = new int[N + 1];
chainLevel = new int[N + 1];
chainParent = new int[N + 1];
adNode = new ArrayList[N + 1];
st = new StringTokenizer(br.readLine());
for(int i=1; i<=N; i++)
{
adNode[i] = new ArrayList<>();
value[i] = Integer.parseInt(st.nextToken());
}
for(int i=1; i<N; i++)
{
st = new StringTokenizer(br.readLine());
int n1 = Integer.parseInt(st.nextToken());
int n2 = Integer.parseInt(st.nextToken());
adNode[n1].add(n2);
adNode[n2].add(n1);
}
// 해당 함수에서 자식 서브트리 크기가 가장 큰 노드를 맨 앞으로 옮김
setSize(1, 0, new int[N + 1]);
chainHead[1] = 1;// 1번 노드의 헤드는 자기자신
// HLD정보 세팅 및 세그먼트 트리 값 업데이트
setHLD(1, 0, 1);
while(Q-->0)
{
st = new StringTokenizer(br.readLine());
int op = Integer.parseInt(st.nextToken());
int i = Integer.parseInt(st.nextToken());
int j = Integer.parseInt(st.nextToken());
if(op == 1)// 1인 경우, i번노드를 j값으로 업데이트
{
update(1, 1, N, treeIdx[i], j);
continue;
}
// 2인 경우, i, j 노드 경로사이의 XOR값을 출력
int ans = 0;
// 같은 체인이 될 때 까지 level을 올림
while(chainHead[i] != chainHead[j])
{
if(chainLevel[i] > chainLevel[j])
{
int tmp = i;
i = j;
j = tmp;
}
ans ^= query(1, 1, N, treeIdx[chainHead[j]], treeIdx[j]);
j = chainParent[j];
}
// 같은 체인에 속하면 그 안에서 다시 한번 해준다.
if(treeIdx[i] > treeIdx[j])
{
int tmp = i;
i = j;
j = tmp;
}
ans ^= query(1, 1, N, treeIdx[i], treeIdx[j]);
sb.append(ans).append('\n');
}
System.out.print(sb);
}
static int query(int treeNode, int s, int e, int left, int right) {
if(e < left || right < s)
return 0;
if(left <= s && e <= right)
return tree[treeNode];
int mid = (s + e) >> 1;
int l = query(treeNode << 1, s, mid, left, right);
int r = query(treeNode << 1 | 1, mid + 1, e, left, right);
return l ^ r;
}
static void update(int treeNode, int s, int e, int targetIdx, int value) {
if(e < targetIdx || targetIdx < s)
return;
if(s == e)
{
tree[treeNode] = value;
return;
}
int mid = (s + e) >> 1;
update(treeNode << 1, s, mid, targetIdx, value);
update(treeNode << 1 | 1, mid + 1, e, targetIdx, value);
tree[treeNode] = tree[treeNode << 1] ^ tree[treeNode << 1 | 1];
}
static void setHLD(int nowNode, int parentNode, int level) {
treeIdx[nowNode] = ++idx;
chainLevel[nowNode] = level;
for(int i=0; i<adNode[nowNode].size(); i++)
{
int next = adNode[nowNode].get(i);
if(next == parentNode)
continue;
if(i == 0) // heavy 노드일 경우 체인 정보 그대로 유지
{
chainHead[next] = chainHead[nowNode];
chainParent[next] = chainParent[nowNode];
setHLD(next, nowNode, level);
}
else // light 노드일 경우 새 체인 생성
{
chainHead[next] = next;
chainParent[next] = nowNode;
setHLD(next, nowNode, level + 1); // 새 체인일 경우 레벨 증가
}
}
update(1, 1, N, treeIdx[nowNode], value[nowNode]);
}
static void setSize(int nowNode, int parentNode, int[] size) {
int heavySize = 0;
int heavyIdx = 0;
size[nowNode] = 1;
for(int i=0; i<adNode[nowNode].size(); i++)
{
int next = adNode[nowNode].get(i);
if(next == parentNode)
continue;
setSize(next, nowNode, size);
size[nowNode] += size[next];
if(heavySize < size[next])
{
heavySize = size[next];
heavyIdx = i;
}
}
if(adNode[nowNode].size() > 0)
Collections.swap(adNode[nowNode], 0, heavyIdx);
}
}
'알고리즘 > HLD(Heavy-Light Decomposition)' 카테고리의 다른 글
BOJ 백준 16453 Linhas de Metrô (0) | 2025.04.28 |
---|---|
BOJ 백준 31234 대역폭 관리 (0) | 2025.04.27 |
BOJ 백준 13309 트리 (0) | 2025.04.26 |
BOJ 백준 13512 트리와 쿼리 3 (0) | 2025.04.26 |
BOJ 백준 13510 트리와 쿼리 1 (0) | 2025.04.24 |