문제
- 문제 링크
BOJ 25491 - Mexor tree
x y z : 정점 x와 y를 잇는 단순 경로 상의 정점들의 값들을 각각 z와 XOR 연산을 시행한 값으로 바꾼다. bi : 정점 S와 정점 i를 잇는 단순 경로 상의 정점들의 값들 중에 존재하지 않는 음이 아닌 정수 중 최솟값.
N개의 정점으로 이루어진 트리와, 각 정점의 정점값이 주어진다.
쿼리의 내용과 수열 bi의 정의가 위와 같을 때, Q개의 쿼리를 모두 처리한 후 b1,b2,...bN 을 출력하자.
TL : 2 sec, ML : 1024 MB
1≤N,Q≤300,000 0≤ai≤N
알고리즘 분류
- 자료 구조 (data_structures)
- 트리를 사용한 집합과 맵 (tree _ set / map)
- 그래프 이론 (graphs)
- 그래프 탐색 (graphs_traversal)
- 깊이 우선 탐색 (dfs)
- 트리 (trees)
- 최소 공통 조상 (lowest common ancestor)
풀이
주어진 쿼리를 크게 두가지 방식으로 처리할 수 있다.
처음 방식이야 구간 단위 업데이트 이므로 Lazy Propagation을 적절히 이용하면 된다.
두번째 방식의 경우 약간의 관찰을 요구한다.
x,y에 대해 각각 루트로부터 이어지는 경로 위 모든 정점에 z를 XOR 했다고 해보자.
그럼 루트로부터 lca(x,y)까지의 정점에는 변화가 없고, 나머지 x↔y 의 경로 위 정점들엔 정상적으로 z가 XOR됨을 관찰할 수 있다.
물론 lca(x,y)도 x↔y 의 경로에 포함되므로 추가적인 처리가 필요하다.
구체적으로 아래의 과정을 따라가자.
정점 i의 XOR 업데이트 상태를 기록하는 배열 rec[i]를 정의하자. imos법과 같은 테크닉에서, 업데이트 포인트를 찍어둔 후 최종 스위핑 한 번으로 결과를 도출할 수 있었다. 위에서의 관찰을 통해, 지금 언급한 테크닉을 트리에서 적용할 수 있게 된다. 업데이트 포인트는 rec[x],rec[y]가 되며 a[lca(x,y)]에 처리해주는 것도 잊지 말아야 한다.
선형 구조에선 단순히 반복문으로 스위핑을 진행했다면, 비선형 구조에선 DFS를 이용하면 된다.
모든 정점들마다 해당 정점의 서브트리의 rec[] 을 받고, 처음 a[]와 XOR 해주어 업데이트를 끝마치자.
이제 S를 시작으로 각 정점의 MEX값만 구하면 끝인데, 내가 사용한 방식의 설명은 아래의 링크로 대체한다.
왜 질문글을 쓰고 나서야 문제점이 보이는지 원..
삽질의 흔적과 독백
전체 코드
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 | #include<bits/stdc++.h> #define N 300001 using namespace std; vector <int> Gr[N]; int rec[N], dep[N], dp[N][20]; int n, s, a[N], b[N]; void Input() { ios_base::sync_with_stdio(0); cin.tie(0); cin >> n >> s; for (int i(1); i <= n; cin >> a[i++]); for (int u, v, i{}; ++i < n;) { cin >> u >> v; Gr[u].push_back(v); Gr[v].push_back(u); } } void LCAInit(int now, int depth) { dep[now] = depth; for (int& next : Gr[now]) if (!dep[next]) { dp[next][0] = now; LCAInit(next, depth + 1); } } void TableDP() { for (int j(1); j < 20; j++) for (int i(1); i <= n; i++) dp[i][j] = dp[dp[i][j - 1]][j - 1]; } int getLCA(int u, int v) { if (dep[u] < dep[v]) swap(u, v); int diff(dep[u] - dep[v]); for (int i{}; diff; diff >>= 1, i++) if (diff & 1) u = dp[u][i]; if (u ^ v) { for (int i(19); !!~i; i--) if (dp[u][i] ^ dp[v][i]) u = dp[u][i], v = dp[v][i]; u = dp[u][0]; } return u; } void Query() { int q; cin >> q; while (q--) { int x, y, z; cin >> x >> y >> z; int lca(getLCA(x, y)); rec[x] ^= z, rec[y] ^= z, a[lca] ^= z; } } void Calxor(int now, int prev) { for (int& next : Gr[now]) if (next ^ prev) { Calxor(next, now); rec[now] ^= rec[next]; } a[now] ^= rec[now]; } set <int> mex; int cnt[N << 1]; void Getmex(int now, int prev) { mex.erase(a[now]); cnt[a[now]]++; b[now] = *mex.begin(); for (int& next : Gr[now]) if (next ^ prev) Getmex(next, now); if (!--cnt[a[now]]) mex.insert(a[now]); } int main() { Input(); LCAInit(s, 1); TableDP(); Query(); Calxor(s, s); for (int i{}; i <= N; mex.insert(i++)); Getmex(s, s); for (int i(1); i <= n; cout << b[i++] << ' '); } | cs |
comment
끝
'Problem Solving > Baekjoon Online Judge (Platinum)' 카테고리의 다른 글
백준 23979 - 트리의 재구성 (C++) (0) | 2023.09.04 |
---|---|
백준 24889 - 통행량 조사 (C++) (0) | 2023.09.01 |
백준 24520 - Meet In The Middle (C++) (0) | 2023.08.29 |
백준 29261 - 소수 세기 (C++) (0) | 2023.08.28 |
백준 25549 - 트리의 MEX (C++) (0) | 2023.08.28 |