레야몬
[C++] 16404번 주식회사 승범이네 - 자료 구조, 그래프 이론, 트리, 세그먼트 트리, 느리게 갱신되는 세그먼트 트리, 오일러 경로 테크닉 본문
[C++] 16404번 주식회사 승범이네 - 자료 구조, 그래프 이론, 트리, 세그먼트 트리, 느리게 갱신되는 세그먼트 트리, 오일러 경로 테크닉
Leyamon 2022. 12. 1. 12:411. 문제
- 모든 판매원은 사수가 배정되며 한 회원당 단 한 명씩만 배정된다. 어떤 회원 A가 손해를 보면 그 회원의 모든 부사수도 같은 손해를 보고, 반대로 이익이 생기면 모든 부사수가 같은 이익을 본다. 승범이는 다음 두 종류의 명령을 처리하려 한다.
- 1 i w : 작은 i가 w만큼 이익/손해를 본다.
- 2 i : 직원 i의 현재 통장 잔액 출력
- 직원들은 빈 통장을 갖고 시작하며, 이익과 손해가 실시간으로 통장에 기록된다. 물론 통장 잔액은 음수일 수도 있다.
<입력>
- -1- 판매원의 수
, 명령의 수- 판매원들은 1~N번호로 매겨지며 승범이는 항상 1이다.
- -2- 1~N 사수가 순서대로 주어진다. 승범이는 사수가 없으므로 -1이다.
- -M개- 명령
<출력>
- 2번 명령이 주어질 때마다 한 줄에 하나씩 해당하는 직원 i의 잔고 상황을 출력한다.
2. 재정의
- 자식이 하나밖에 없는 트리에서(부모 노드 제외) 한 노드와 그 모든 자식 노드의 값을 변화시킬 때 어느 노드의 값을 출력하라.
3. 해결 방법
- adj[a].push_back(b)로 그래프 완성
- dfs로 그래프를 탐색할 때 번호를 매기고 이 번호로 제일 처음 번호와 끝 번을 se<pair<int, int>>에 저장
- 느리게 갱신되는 세그먼트 트리를 수행
4. 실수한 점, 개선할 점
- dfs로 탐색한 번호를 dfs_num에 메모했는데 node랑 dfs_num이랑 햇갈려서 실수를 많이 했다.
- 팬윅 트리를 쓰면 시간이 단축되는 것 같은데... 뭐 얼마 단축 안되니까 상관없겠지?
<코드>
#include <iostream>
#include <cmath>
#include <vector>
#define pii pair<int, int>
#define Fi first
#define Se second
using namespace std;
typedef long long ll;
const int MAX_N = 100001;
const int MAX_Node = 1 << ((int)ceil(log2(MAX_N))+1);
// <문제>
// 판매원의 수, 명령의 수
int N, M;
// <그래프>
// 판매원 그래프
vector<int> adj[MAX_N];
// dfs로 탐색했을 때 번호와 시작과 끝
int dfs_num[MAX_N];
vector<pii> se(MAX_N);
int dfs_cnt;
// <Segment Tree Lazy Propagation>
ll tree[MAX_Node], lazy[MAX_Node];
void update_lazy(int node, int start, int end) {
if(lazy[node]) {
tree[node] += (end-start+1)*lazy[node];
if(start != end) {
lazy[node*2] += lazy[node];
lazy[node*2+1] += lazy[node];
}
lazy[node] = 0;
}
}
void update_range(int node, int start, int end, int left, int right, ll diff) {
update_lazy(node, start, end);
if(end < left || right < start)
return;
if(left <= start && end <= right) {
tree[node] += (end-start+1)*diff;
if(end != start) {
lazy[node*2] += diff;
lazy[node*2+1] += diff;
}
return;
}
update_range(node*2, start, (start+end)/2, left, right, diff);
update_range(node*2+1, (start+end)/2+1, end, left, right, diff);
tree[node] = tree[node*2] + tree[node*2+1];
}
ll query(int node, int start, int end, int left, int right) {
update_lazy(node, start, end);
if(end < left || right < start)
return 0;
if(left <= start && end <= right)
return tree[node];
ll lsum = query(node*2, start, (start+end)/2, left, right);
ll rsum = query(node*2+1, (start+end)/2+1, end, left, right);
return lsum + rsum;
}
int dfs(int node) {
// dfs 번호 매기기
dfs_num[node] = ++dfs_cnt;
int last = dfs_num[node];
// 반환한 끝 번호 값을 last에 저장
for(int i=0; i<adj[node].size(); i++)
last = dfs(adj[node][i]);
// 시작과 끝 저장
se[node] = {dfs_num[node], last};
return last;
}
void input() {
cin >> N >> M;
// 그래프 만들기
for(int i=1; i<=N; i++) {
int node;
cin >> node;
adj[node].push_back(i);
}
// dfs로 번호 매기기, 시작과 끝 찾기
dfs(1);
}
int main() {
ios_base::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
input();
for(int i=0; i<M; i++) {
int what;
cin >> what;
if(what == 1) {
int a, w;
cin >> a >> w;
update_range(1, 0, N-1, se[a].Fi-1, se[a].Se-1, w);
}
else {
int a;
cin >> a;
cout << query(1, 0, N-1, dfs_num[a]-1, dfs_num[a]-1) << '\n';
}
}
return 0;
}
<오일러 경로 테크닉(Euler Tour Technique)>
https://david0506.tistory.com/55
오일러 경로 테크닉(Euler Tour Technique)
오일러 경로 테크닉이란? dfs로 트리를 순회해서 방문하는 순서대로 번호를 다시 지정해주고 노드에 진입한 시점과 빠져나간 시점을 기록하여 구간을 관리하는 방법이다. 일반적으로 구간 쿼리
david0506.tistory.com
<ceil() 함수>
https://blockdmask.tistory.com/112
[C언어/C++] 올림, 내림, 반올림 (floor, ceil) 함수
안녕하세요 BlockDMask 입니다. 오늘은 올림, 내림 을 할수있는 ceil, floor 함수에 대해서 알아보고. floor 함수를 통해서 반올림을 하는 것 까지 보도록 하겠습니다. C의 함수들이 C++에 호환이 되어서 C
blockdmask.tistory.com
<느리게 갱신되는 세그먼트 트리(Segment Tree with Lazy Propagation)>
https://book.acmicpc.net/ds/segment-tree-lazy-propagation
느리게 갱신되는 세그먼트 트리
소스 1void update_range(vector &tree, int node, int start, int end, int left, int right, long long diff) { if (left > end || right < start) { return; } if (start == end) { tree[node] += diff; return; } update_range(tree, node*2, start, (start+end)/2, lef
book.acmicpc.net
※현재 고등학교 등교 중인 학생입니다. 이제 알고리즘을 본격적으로 공부하기 시작해서 아직 초보입니다. 혹시 제가 잘못 알고 있는 점이나 더 좋은 풀이 방법이 있어 댓글에 남겨주시면 감사히 하나하나 열심히 읽어보겠습니다. 좋아요, 단순한 댓글 한마디라도 저에겐 큰 힘이 됩니다! 감사합니다.
'알고리즘 > 백준' 카테고리의 다른 글
[C++] 2447번 별 찍기 - 10 - 분할 정복, 재귀 (0) | 2022.12.01 |
---|---|
[C++] 18227번 성대나라의 물탱크 - 자료 구조, 트리, 세그먼트 트리, 오일러 경로 테크닉 (0) | 2022.12.01 |
[C++] 16978번 수열과 쿼리 22 - 자료 구조, 세그먼트 트리, 오프라인 쿼리 (0) | 2022.11.18 |
[C++] 13548번 수열과 쿼리 6 - 오프라인 쿼리, mo's (0) | 2022.11.13 |
[C++] 13547번 수열과 쿼리 5 - 오프라인 쿼리, mo's (0) | 2022.11.11 |