반응형
리뷰
https://www.acmicpc.net/problem/2820
오일러 경로 테크닉을 통해 배열을 트리구조로 재정의 하고, 해당 정보를 이용해 세그먼트 트리를 만든다.
세그먼트 트리 상에서 LazyPropagation을 통해 업데이트를 진행하고 각 쿼리마다 직원의 월급을 출력하는 문제
전역 변수
- MAX_N : n이 입력될 수 있는 최대값, 500001로 초기화 한다.
- n, m : 직원의 개수 n, 쿼리의 개수 m
- nodes : 각 직원의 초기 월급을 저장할 정수형 배열
- tree : 세그먼트 트리를 저장할 정수형 배열
- lazy : 세그먼트 트리의 업데이트 정보를 저장할 정수형 배열
- it : 오일러 경로 탐색을 통해 직원의 시작 인덱스를 저장하기 위한 정수형 배열
- sal : 오일러 경로에서 구한 인덱스를 기준으로 초기 월급을 재산정 하기 위한 배열
- ot : 오일러 경로 탐색을 통해 직원의 종료 인덱스를 저장하기 위한 정수형 배열
- t : 오일러 경로 탐색 시 인덱스로 사용할 변수
- parent : 트리의 간선을 저장하기 위한 정수형 벡터
함수
1. dfs
void dfs(int cn)
오일러 경로 탐색을 통해 직원의 인덱스와 초기 월급을 초기화 하는 함수
- 매개변수로 현재 탐색할 노드의 번호를 입력 받는다.
- it의 cn인덱스에 t를 전위증가연산한 값을 저장해 준다.
- sal의 t인덱스에 cn의 초기 월급인 nodes의 cn인덱스 값을 저장해 준다.
- parent의 cn인덱스를 순회하며 자식의 노드 번호를 dfs함수의 매개변수로 전달하여 재귀를 진행한다.
- 재귀 진행 및 for문이 종료되었다면 ot의 cn인덱스에 t값을 저장해 준다.
2. build
void build(int node, int s, int e)
세그먼트 트리의 초기화를 위한 함수
- 리프 노드에 도달하면 트리의 노드값을 sal의 s인덱스로 저장해 준다.
- 리프 노드에 도달하지 못했다면 좌 우 자식 노드로 재귀탐색을 진행해 준다.
- 사실상 구간합은 필요가 없으나 그냥 넣어주었다.
3. propagate
void propagate(int node, int s, int e)
세그먼트 트리에 느린 업데이트를 진행하는 함수
- lazy의 노드에 값이 존재한다면 세그먼트 트리의 e - s + 1구간에 값을 더해준다.
- 만약 리프노드가 아니라면 하위 lazy의 하위 노드에 lazy값을 전파해준다.
- 업데이트 적용이 완료되었다면 lazy값을 0으로 초기화 해준다.
4. update
void update(int node, int s, int e, int L, int R, int val)
세그먼트 트리의 구간 업데이트를 진행할 함수
- 함수 실행 시 현재 노드, 탐색범위를 매개변수로 전달하여 propagate함수를 실행해 준다.
- 만약 범위를 벗어난 탐색 범위라면 리턴 처리해 준다.
- 업데이트를 적용할 구간과 탐색 구간이 겹친다면 lazy의 노드에 val을 더해준 뒤 propagate함수를 실행하고 리턴한다.
- 구간이 겹치지 않는다면 마찬가지로 mid로 잘라 좌, 우 자식노드로 재귀 탐색을 진행한다.
5. query
int query(int node, int s, int e, int idx)
특정 직원의 월급을 리턴하는 함수
- 함수 실행 시 update와 마찬가지로 propagate함수를 실행해 준다.
- 만약 리프노드에 도달했다면 세그먼트 트리 상의 해당 노드의 값을 리턴해 준다.
- 리프 노드가 아니라면 탐색 구간을 반으로 갈라 idx가 위치한 방향쪽으로 재귀 탐색을 진행해 준다.
문제풀이
- n, m값을 입력 받고, 가장 상사인 1번 노드의 초기 월급을 nodes[1]에 입력 받는다.
- 2 ~ n번 직원까지 반복하여 초기 월급을 nodes배열에, 상사 정보를 parent벡터에 초기화 해준다.
- dfs함수를 가장 상사인 1번 직원부터 실행하여 it, ot, sal배열을 초기화 해준다.
- build함수를 통해 sal배열을 기준으로 세그먼트 트리를 초기화 해준다.
- m개의 쿼리를 수행하며 order에 p, u를 입력 받아준다.
- order가 p일 경우 idx, val을 추가로 입력 받아 세그먼트 트리 업데이트를 진행해 준다.
- order가 u일 경우 idx를 추가로 입력 받아 idx번째 직원의 월급을 출력해 준다.
참고 사항
p a x가 주어진 경우 a의 모든 부하의 월급을 x만큼 증가시킨다. (-10,000 ≤ x ≤ 10,000)
업데이트 쿼리 시 자기 자신은 포함하지 않는다, it가 자기의 뒤인 경우부터 증가시켜야 한다.
모든 직원의 월급은 항상 양의 정수이고 2^31-1 이하이다.
int범위 내에 문제 해결이 가능하다.
nodes배열에 받은 초기 월급 정보로 세그먼트 트리를 초기화 하면 안된다.
오일러 경로를 탐색하면서 직원의 인덱스가 변경되므로 sal과 같이 별도의 배열을 초기화 해주어야 한다.
정답 코드
#include<iostream>
#include<vector>
using namespace std;
const int MAX_N = 500001;
int n, m;
int nodes[MAX_N];
int tree[MAX_N * 4];
int lazy[MAX_N * 4];
int it[MAX_N];
int sal[MAX_N];
int ot[MAX_N];
int t;
vector<int> parent[MAX_N];
void dfs(int cn) {
it[cn] = ++t;
sal[t] = nodes[cn];
for (int nn : parent[cn]) dfs(nn);
ot[cn] = t;
}
void build(int node, int s, int e) {
if (s == e) tree[node] = sal[s];
else {
int mid = (s + e) / 2;
build(node * 2, s, mid);
build(node * 2 + 1, mid + 1, e);
tree[node] = tree[node * 2] + tree[node * 2 + 1];
}
}
void propagate(int node, int s, int e) {
if (lazy[node]) {
tree[node] += (e - s + 1) * lazy[node];
if (s != e) {
lazy[node * 2] += lazy[node];
lazy[node * 2 + 1] += lazy[node];
}
lazy[node] = 0;
}
}
void update(int node, int s, int e, int L, int R, int val) {
propagate(node, s, e);
if (R < s || e < L) return;
if (L <= s && e <= R) {
lazy[node] += val;
propagate(node, s, e);
return;
}
int mid = (s + e) / 2;
update(node * 2, s, mid, L, R, val);
update(node * 2 + 1, mid + 1, e, L, R, val);
tree[node] = tree[node * 2] + tree[node * 2 + 1];
}
int query(int node, int s, int e, int idx) {
propagate(node, s, e);
if (s == e) return tree[node];
int mid = (s + e) / 2;
if (idx <= mid) return query(node * 2, s, mid, idx);
return query(node * 2 + 1, mid + 1, e, idx);
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n >> m >> nodes[1];
for (int i = 2; i <= n; i++) {
int par; cin >> nodes[i] >> par;
parent[par].push_back(i);
}
dfs(1);
build(1, 1, n);
while (m--) {
char order; cin >> order;
if (order == 'p') {
int idx, val; cin >> idx >> val;
update(1, 1, n, it[idx] + 1, ot[idx], val);
}
else {
int idx; cin >> idx;
cout << query(1, 1, n, it[idx]) << "\n";
}
}
}
728x90
반응형
'알고리즘 공부 > C++' 카테고리의 다른 글
[G4] 백준 14500번 테트로미노 C++ 구현, 백트래킹 (3) | 2024.11.09 |
---|---|
[G3] 백준 16637번 괄호 추가하기 C++ 구현, 백트래킹 (0) | 2024.11.08 |
[P5] 백준 1989번 부분배열 고르기 2 C++ 세그먼트 트리 (1) | 2024.11.06 |
[P5] 백준 2104번 부분배열 고르기 C++ 세그먼트 트리 (0) | 2024.11.06 |
[P5] 백준 1725번 히스토그램 C++ 세그먼트 트리 (0) | 2024.11.06 |