SlideShare a Scribd company logo
#4—
퍼시스턴트세그먼트트리
Persistent Segment Tree
by
Suhyun Park
Sogang ICPC Team – 2020 겨울 고급 스터디 acm.sogang.ac.kr
Remarks on Segment Trees
Sogang ICPC Team 1
1–10
1–5 6–10
1–3 4–5 6–8 9–10
1–2 3 4 5 6–7 8 9 10
1 2 6 7
f (x, s, e, · · · ) = f
(
2x, s,
⌊
s + e
2
⌋
, · · ·
)
+ f
(
2x + 1,
⌊
s + e
2
⌋
+ 1, e, · · ·
)
Persistent Segment Tree
Sogang ICPC Team 2
T1
1–5
1–3 4–5
1–2 3 4 5
1 2
구조체와 포인터로 구현하며, 2D 쿼리를 처리하기 위해 여러 버전의 트리를 만들 예정
예를 들어 Tn 은 y 좌표가 0 · · · n인 쿼리의 정보를 담는다고 하자
Persistent Segment Tree
Sogang ICPC Team 3
T0 T1
1–5
1–3 4–5
1–2 3 4 5
1 2
1–5
1–3 4–5
1–2 3
T2 는 T1 을 기반으로 다른 노드들만 새로 만들고, 나머지 노드들은 포인터로 전 버전의
트리를 가리키게 한다, 이를 반복해 트리 생성
Persistent Segment Tree
Sogang ICPC Team 4
총 데이터 수를 n개라 할 때· · ·
✓ 트리 하나 만드는 시간: O (log xmax)
✓ 트리 전체를 만드는 시간: O (n log xmax)
✓ Ti 를 쿼리하는 시간: O (log xmax)
Persistent Segment Tree
Sogang ICPC Team 5
R = [xl, xr] × [yl, yr] 쿼리하기?
✓ Tn 은 y 좌표가 0 · · · n인 쿼리의 정보를 담는다
✓ 따라서 R을 쿼리하려면
– Tyr 에 대해서 [xl, xr]을 쿼리
– Tyl−1 에 대해서 [xl, xr]을 쿼리
– 한 후 둘의 차를 구하면 된다
Ti 에 대해 두 번 쿼리하므로 직사각형 영역의 쿼리가 겨우 O (log mx)이다!
PSTs vs 2D Segment Trees
Sogang ICPC Team 6
0 ≤ x ≤ mx, 0 ≤ y ≤ my 이고 총 데이터 수를 n개라 할 때· · ·
PST 2D
공간 O (n log mx) O (mxmy)
쿼리 O (log n) O (log mx log my)
PSTs vs 2D Segment Trees
Sogang ICPC Team 7
하지만
✓ 값 업데이트
✓ RMQ 처리
같은 일들은 PST가 할 수 없는 부분이고, 값들이 sparse하지 않으면 의미가 별로 없으므로
PST와 2D segment tree 중 적절한 자료구조를 잘 선택해야 한다
PSTs vs 2D Segment Trees
Sogang ICPC Team 8
참고: 2D segment tree는 2차원 배열을 이용해 y좌표로 분할 정복 → x좌표로 분할 정복
같은 식으로 만들 수 있다
구현이 생각보다 어렵지 않으니 직접 해 봐도 좋음! (lazy propagation 등 모두 1D
segment tree와 비슷하게 처리하면 된다)
2D segment tree source code
Egg #
11012
Sogang ICPC Team 9
당신은 임기가 m일 남은 대통령이고, 매일 유세하러 나간다. 유세는 직사각형 모양의
공간을 돌아다니며 진행한다.
시민들은 집에서 당신이 보이면 달걀을 던진다. 시민들의 집의 좌표들이 주어졌을 때,
유세하러 나가면 몇 개의 달걀을 맞을지 구하라.
✓ 집 개수 = n ≤ 10000, m ≤ 50000
✓ 0 ≤ x, y ≤ 105
✓ 테스트 케이스 20개
Egg #
11012
Sogang ICPC Team 10
문제를 간단히 하면
✓ R = [xl, xr] × [yl, yr] 내부의 점의 개수를 세는 쿼리를 구현해라
가 된다
Egg #
11012
Sogang ICPC Team 11
15 struct pst {
16 struct pst_node {
17 int l, r;
18 int sum;
19
20 pst_node() : l(-1), r(-1), sum(0) {}
21
22 pst_node(int val) : l(-1), r(-1), sum(val) {}
23
24 pst_node(int l, int r, int sum) : l(l), r(r), sum(sum) {}
25 };
26
27 int xn;
28 vector<pst_node> nodes;
29 vector<int> root_indexes;
source code
그냥 세그먼트 트리 만들듯이
Egg #
11012
Sogang ICPC Team 12
35 int init(int s, int e) {
36 if (s == e) {
37 nodes.emplace_back(0);
38 } else {
39 int m = (s + e) / 2;
40 int l = init(s, m), r = init(m + 1, e);
41 nodes.emplace_back(l, r, 0);
42 }
43 return nodes.size() - 1;
44 }
source code
그냥 세그먼트 트리 만들듯이
Egg #
11012
Sogang ICPC Team 13
46 int _sum(int x, int s, int e, int l, int r) {
47 pst_node u = nodes[x];
48 if (l > r) return 0;
49 if (e < l || r < s) return 0;
50 if (l <= s && e <= r) return u.sum;
51 int m = (s + e) / 2;
52 return _sum(u.l, s, m, l, r)
53 + _sum(u.r, m + 1, e, l, r);
54 }
source code
그냥 세그먼트 트리 만들듯이
Egg #
11012
Sogang ICPC Team 14
56 int _add(int x, int s, int e, int i, int dv) {
57 pst_node u = nodes[x];
58 if (s == e) {
59 nodes.emplace_back(u.sum + dv);
60 } else {
61 int m = (s + e) / 2;
62 if (i <= m) {
63 int l = _add(u.l, s, m, i, dv);
64 nodes.emplace_back(l, u.r, u.sum + dv);
65 } else {
66 int r = _add(u.r, m + 1, e, i, dv);
67 nodes.emplace_back(u.l, r, u.sum + dv);
68 }
69 }
70 return nodes.size() - 1;
71 }
source code
여기서는 갱신되는 부분만 새로 만들어준다
Egg #
11012
Sogang ICPC Team 15
86 void add_back() {
87 root_indexes.emplace_back(root_indexes.back());
88 }
source code
트리 버전 업 – root_indexes에는 버전마다 루트의 인덱스들이 들어 있음
Egg #
11012
Sogang ICPC Team 16
101 priority_queue<pii, vector<pii>, greater<>> coords;
102 while (n--) {
103 int x, y;
104 cin >> x >> y;
105 coords.emplace(x, y);
106 }
source code
좌표 입력
Egg #
11012
Sogang ICPC Team 17
108 pst tree(100000);
109 for (int i = 0; i <= 100000; i++) {
110 while (coords.size() && coords.top().first == i) {
111 tree.update_back(coords.top().second, 1);
112 coords.pop();
113 }
114 tree.add_back();
115 }
source code
PST 생성
Egg #
11012
Sogang ICPC Team 18
117 int s = 0;
118 while (q--) {
119 int l, r, b, t;
120 cin >> l >> r >> b >> t;
121
122 s += tree.sum(b, t, l, r);
123 }
124 cout << s << 'n';
source code
쿼리 처리
Egg #
11012
Sogang ICPC Team 19
73 int sum(int xl, int xr, int y) {
74 return _sum(root_indexes[y], 0, xn, xl, xr);
75 }
76
77 int sum(int xl, int xr, int yl, int yr) {
78 if (yl <= 0) return sum(xl, xr, yr);
79 return sum(xl, xr, yr) - sum(xl, xr, yl - 1);
80 }
source code
쿼리 처리
PST로 더 할 수 있는 것
Sogang ICPC Team 20
✓ 구간에서 k번째 원소 구하기
– 원소의 값들을 좌표 압축하고 구간 내에서 이분 탐색
✓ 물론 이 트리도 HLD와 섞을 수 있다
K번째 수 #
7469
Sogang ICPC Team 21
✓ Q (i, j, k): 배열 a [i · · · j]를 정렬했을 때, k번째 수를 리턴하는 함수
를 구현해라
K번째 수 #
7469
Sogang ICPC Team 22
Q (i, j, k): 배열 a [i · · · j]를 정렬했을 때, k번째 수를 리턴하는 함수
✓ 배열에 저장된 정수의 값들을 좌표 압축 하듯이 압축하고
– zip : a [i] = v → v′
✓ 인덱스를 i, 압축된 값을 v′
라고 했을 때 배열의 원소 각각을
(
v′
, i
)
라는 점들로
생각한다면
✓ [0, x] × [i, j]에 속한 점의 개수가 k개가 되는 x를 찾으면 된다
K번째 수 #
7469
Sogang ICPC Team 23
[0, x] × [i, j]에 속한 점의 개수가 k개가 되는 x를 찾으면 된다
✓ PST의 경우 기본적으로 [xa, xb] × [0, y]의 쿼리를 처리하므로
✓
(
[0, x] × [0, j] 에 속한 점의 개수
)
−
(
[0, x] × [0, i − 1] 에 속한 점의 개수
)
를 계산
– 인덱스 트리 쓰듯이
– 쿼리 한 번에 O (log xmax)
K번째 수 #
7469
Sogang ICPC Team 24
82 int _kth(int yli, int yri, int s, int e, int k) {
83 if (s == e) return s;
84 int p = nodes[nodes[yri].l].sum - nodes[nodes[yli].l].sum;
85 if (k < p) {
86 return _kth(nodes[yli].l, nodes[yri].l, s, (s + e) / 2, k);
87 } else {
88 return _kth(nodes[yli].r, nodes[yri].r, (s + e) / 2 + 1, e, k - p);
89 }
90 }
91
92 int kth(int yl, int yr, int k) {
93 return _kth(root_indexes[yl - 1], root_indexes[yr], 0, xn, k);
94 }
source code
K번째 수 #
7469
Sogang ICPC Team 25
116 for (int i = 0; i < n; i++) {
117 cin >> a[i];
118 zip[a[i]] = 1;
119 }
120
121 int c = 1;
122 for (const auto& kv : zip) {
123 int i = kv.first;
124 zip[i] = c;
125 unzip[c] = i;
126 c++;
127 }
source code
K번째 수 #
7469
Sogang ICPC Team 26
129 pst tree(100000);
130
131 for (int i = 0; i < n; i++) { // (x, y) = (v', i)
132 tree.add_back();
133 tree.update_back(zip[a[i]], 1);
134 }
source code
(
v′
, i
)
임에 주의
K번째 수 #
7469
Sogang ICPC Team 27
136 while (m--) {
137 int i, j, k;
138 cin >> i >> j >> k;
139 cout << unzip[tree.kth(i, j, k - 1)] << 'n';
140 }
source code
4회차 연습문제
Sogang ICPC Team 28
✓ Egg #
11012
✓ K번째 수 #
7469
1. 수열과 쿼리 22 #
16978
2. 트리와 쿼리 8 #
13517 = 트리와 K번째 수 #
11932
3. XOR 쿼리 #
13538
4. 히스토그램에서 가장 큰 직사각형과 쿼리 #
16977

More Related Content

What's hot

Unity ml agent quick guide
Unity ml agent quick guideUnity ml agent quick guide
Unity ml agent quick guide
Kyoungman Lee
 
アルゴリズムイントロダクション15章 動的計画法
アルゴリズムイントロダクション15章 動的計画法アルゴリズムイントロダクション15章 動的計画法
アルゴリズムイントロダクション15章 動的計画法
nitoyon
 
動的計画法を極める!
動的計画法を極める!動的計画法を極める!
그림 그리는 AI
그림 그리는 AI그림 그리는 AI
그림 그리는 AI
NAVER Engineering
 
ウェーブレット木の世界
ウェーブレット木の世界ウェーブレット木の世界
ウェーブレット木の世界
Preferred Networks
 
高速フーリエ変換
高速フーリエ変換高速フーリエ変換
高速フーリエ変換
AtCoder Inc.
 
[NDC 2021] 게임 PD가 되어 보니
[NDC 2021] 게임 PD가 되어 보니[NDC 2021] 게임 PD가 되어 보니
[NDC 2021] 게임 PD가 되어 보니
Yongha Kim
 
ゲームAI入門(後半)
ゲームAI入門(後半)ゲームAI入門(後半)
ゲームAI入門(後半)
Youichiro Miyake
 
動的計画法
動的計画法動的計画法
취미로 엔진 만들기
취미로 엔진 만들기취미로 엔진 만들기
취미로 엔진 만들기
Jiho Choi
 
競技プログラミングにおけるコードの書き方とその利便性
競技プログラミングにおけるコードの書き方とその利便性競技プログラミングにおけるコードの書き方とその利便性
競技プログラミングにおけるコードの書き方とその利便性
Hibiki Yamashiro
 
HATETRISを攻略するAIを作る
HATETRISを攻略するAIを作るHATETRISを攻略するAIを作る
HATETRISを攻略するAIを作る
threepipes_s
 
3次元の凸包を求める
3次元の凸包を求める3次元の凸包を求める
3次元の凸包を求める
abc3141
 
딥러닝과 강화 학습으로 나보다 잘하는 쿠키런 AI 구현하기
딥러닝과 강화 학습으로 나보다 잘하는 쿠키런 AI 구현하기딥러닝과 강화 학습으로 나보다 잘하는 쿠키런 AI 구현하기
딥러닝과 강화 학습으로 나보다 잘하는 쿠키런 AI 구현하기
NAVER D2
 
SSE4.2の文字列処理命令の紹介
SSE4.2の文字列処理命令の紹介SSE4.2の文字列処理命令の紹介
SSE4.2の文字列処理命令の紹介
MITSUNARI Shigeo
 
C++コミュニティーの中心でC++をDISる
C++コミュニティーの中心でC++をDISるC++コミュニティーの中心でC++をDISる
C++コミュニティーの中心でC++をDISる
Hideyuki Tanaka
 
게임업계에서 내가 하고 싶은 일 찾는 방법
게임업계에서 내가 하고 싶은 일 찾는 방법게임업계에서 내가 하고 싶은 일 찾는 방법
게임업계에서 내가 하고 싶은 일 찾는 방법
Donghun Lee
 
Convex Hull Trick
Convex Hull TrickConvex Hull Trick
Palindromic tree
Palindromic treePalindromic tree
Palindromic tree
__math
 
김동건, 게임개발에 남은 기회들, KOG 발표
김동건, 게임개발에 남은 기회들, KOG 발표김동건, 게임개발에 남은 기회들, KOG 발표
김동건, 게임개발에 남은 기회들, KOG 발표devCAT Studio, NEXON
 

What's hot (20)

Unity ml agent quick guide
Unity ml agent quick guideUnity ml agent quick guide
Unity ml agent quick guide
 
アルゴリズムイントロダクション15章 動的計画法
アルゴリズムイントロダクション15章 動的計画法アルゴリズムイントロダクション15章 動的計画法
アルゴリズムイントロダクション15章 動的計画法
 
動的計画法を極める!
動的計画法を極める!動的計画法を極める!
動的計画法を極める!
 
그림 그리는 AI
그림 그리는 AI그림 그리는 AI
그림 그리는 AI
 
ウェーブレット木の世界
ウェーブレット木の世界ウェーブレット木の世界
ウェーブレット木の世界
 
高速フーリエ変換
高速フーリエ変換高速フーリエ変換
高速フーリエ変換
 
[NDC 2021] 게임 PD가 되어 보니
[NDC 2021] 게임 PD가 되어 보니[NDC 2021] 게임 PD가 되어 보니
[NDC 2021] 게임 PD가 되어 보니
 
ゲームAI入門(後半)
ゲームAI入門(後半)ゲームAI入門(後半)
ゲームAI入門(後半)
 
動的計画法
動的計画法動的計画法
動的計画法
 
취미로 엔진 만들기
취미로 엔진 만들기취미로 엔진 만들기
취미로 엔진 만들기
 
競技プログラミングにおけるコードの書き方とその利便性
競技プログラミングにおけるコードの書き方とその利便性競技プログラミングにおけるコードの書き方とその利便性
競技プログラミングにおけるコードの書き方とその利便性
 
HATETRISを攻略するAIを作る
HATETRISを攻略するAIを作るHATETRISを攻略するAIを作る
HATETRISを攻略するAIを作る
 
3次元の凸包を求める
3次元の凸包を求める3次元の凸包を求める
3次元の凸包を求める
 
딥러닝과 강화 학습으로 나보다 잘하는 쿠키런 AI 구현하기
딥러닝과 강화 학습으로 나보다 잘하는 쿠키런 AI 구현하기딥러닝과 강화 학습으로 나보다 잘하는 쿠키런 AI 구현하기
딥러닝과 강화 학습으로 나보다 잘하는 쿠키런 AI 구현하기
 
SSE4.2の文字列処理命令の紹介
SSE4.2の文字列処理命令の紹介SSE4.2の文字列処理命令の紹介
SSE4.2の文字列処理命令の紹介
 
C++コミュニティーの中心でC++をDISる
C++コミュニティーの中心でC++をDISるC++コミュニティーの中心でC++をDISる
C++コミュニティーの中心でC++をDISる
 
게임업계에서 내가 하고 싶은 일 찾는 방법
게임업계에서 내가 하고 싶은 일 찾는 방법게임업계에서 내가 하고 싶은 일 찾는 방법
게임업계에서 내가 하고 싶은 일 찾는 방법
 
Convex Hull Trick
Convex Hull TrickConvex Hull Trick
Convex Hull Trick
 
Palindromic tree
Palindromic treePalindromic tree
Palindromic tree
 
김동건, 게임개발에 남은 기회들, KOG 발표
김동건, 게임개발에 남은 기회들, KOG 발표김동건, 게임개발에 남은 기회들, KOG 발표
김동건, 게임개발에 남은 기회들, KOG 발표
 

Similar to 퍼시스턴트 세그먼트 트리 - Sogang ICPC Team, 2020 Winter

Persistent Segment Tree - Sogang ICPC Team, 2019
Persistent Segment Tree - Sogang ICPC Team, 2019Persistent Segment Tree - Sogang ICPC Team, 2019
Persistent Segment Tree - Sogang ICPC Team, 2019
Suhyun Park
 
Lazy Propagation on Segment Trees - Sogang ICPC Team, 2019
Lazy Propagation on Segment Trees - Sogang ICPC Team, 2019Lazy Propagation on Segment Trees - Sogang ICPC Team, 2019
Lazy Propagation on Segment Trees - Sogang ICPC Team, 2019
Suhyun Park
 
12 1. multi-dimensional array
12 1. multi-dimensional array12 1. multi-dimensional array
12 1. multi-dimensional array웅식 전
 
고등학생 R&E Python summary for test
고등학생 R&E Python summary for test고등학생 R&E Python summary for test
고등학생 R&E Python summary for test
Kyunghoon Kim
 
Tensorflow regression 텐서플로우 회귀
Tensorflow regression 텐서플로우 회귀Tensorflow regression 텐서플로우 회귀
Tensorflow regression 텐서플로우 회귀
beom kyun choi
 
자료구조5보고서
자료구조5보고서자료구조5보고서
자료구조5보고서KimChangHoen
 
Project#5 최단거리 찾기 D0 Hwp
Project#5 최단거리 찾기 D0 HwpProject#5 최단거리 찾기 D0 Hwp
Project#5 최단거리 찾기 D0 HwpKimjeongmoo
 
알고리즘과 자료구조
알고리즘과 자료구조알고리즘과 자료구조
알고리즘과 자료구조
영기 김
 
자료구조 프로젝트
자료구조 프로젝트자료구조 프로젝트
자료구조 프로젝트
hyungoh kim
 
R 스터디 첫번째
R 스터디 첫번째R 스터디 첫번째
R 스터디 첫번째
Jaeseok Park
 
하스켈 성능 튜닝
하스켈 성능 튜닝하스켈 성능 튜닝
하스켈 성능 튜닝
민석 이
 
Code로 이해하는 RNN
Code로 이해하는 RNNCode로 이해하는 RNN
Code로 이해하는 RNN
SANG WON PARK
 
하스켈로 알고리즘 문제 풀기 2
하스켈로 알고리즘 문제 풀기 2하스켈로 알고리즘 문제 풀기 2
하스켈로 알고리즘 문제 풀기 2
민석 이
 
2012 Ds B2 02
2012 Ds B2 022012 Ds B2 02
2012 Ds B2 02chl132435
 
2012 Ds B2 02 Pdf
2012 Ds B2 02 Pdf2012 Ds B2 02 Pdf
2012 Ds B2 02 Pdfkd19h
 
[SOPT] 데이터 구조 및 알고리즘 스터디 - #01 : 개요, 점근적 복잡도, 배열, 연결리스트
[SOPT] 데이터 구조 및 알고리즘 스터디 - #01 : 개요, 점근적 복잡도, 배열, 연결리스트[SOPT] 데이터 구조 및 알고리즘 스터디 - #01 : 개요, 점근적 복잡도, 배열, 연결리스트
[SOPT] 데이터 구조 및 알고리즘 스터디 - #01 : 개요, 점근적 복잡도, 배열, 연결리스트
S.O.P.T - Shout Our Passion Together
 
Probabilistic data structures
Probabilistic data structuresProbabilistic data structures
Probabilistic data structures
Sanghyeon Seo
 

Similar to 퍼시스턴트 세그먼트 트리 - Sogang ICPC Team, 2020 Winter (20)

Persistent Segment Tree - Sogang ICPC Team, 2019
Persistent Segment Tree - Sogang ICPC Team, 2019Persistent Segment Tree - Sogang ICPC Team, 2019
Persistent Segment Tree - Sogang ICPC Team, 2019
 
Lazy Propagation on Segment Trees - Sogang ICPC Team, 2019
Lazy Propagation on Segment Trees - Sogang ICPC Team, 2019Lazy Propagation on Segment Trees - Sogang ICPC Team, 2019
Lazy Propagation on Segment Trees - Sogang ICPC Team, 2019
 
12 1. multi-dimensional array
12 1. multi-dimensional array12 1. multi-dimensional array
12 1. multi-dimensional array
 
고등학생 R&E Python summary for test
고등학생 R&E Python summary for test고등학생 R&E Python summary for test
고등학생 R&E Python summary for test
 
Tensorflow regression 텐서플로우 회귀
Tensorflow regression 텐서플로우 회귀Tensorflow regression 텐서플로우 회귀
Tensorflow regression 텐서플로우 회귀
 
자료구조5보고서
자료구조5보고서자료구조5보고서
자료구조5보고서
 
Project#5 최단거리 찾기 D0 Hwp
Project#5 최단거리 찾기 D0 HwpProject#5 최단거리 찾기 D0 Hwp
Project#5 최단거리 찾기 D0 Hwp
 
알고리즘과 자료구조
알고리즘과 자료구조알고리즘과 자료구조
알고리즘과 자료구조
 
자료구조 프로젝트
자료구조 프로젝트자료구조 프로젝트
자료구조 프로젝트
 
R 스터디 첫번째
R 스터디 첫번째R 스터디 첫번째
R 스터디 첫번째
 
하스켈 성능 튜닝
하스켈 성능 튜닝하스켈 성능 튜닝
하스켈 성능 튜닝
 
Code로 이해하는 RNN
Code로 이해하는 RNNCode로 이해하는 RNN
Code로 이해하는 RNN
 
Rdatamining
Rdatamining Rdatamining
Rdatamining
 
하스켈로 알고리즘 문제 풀기 2
하스켈로 알고리즘 문제 풀기 2하스켈로 알고리즘 문제 풀기 2
하스켈로 알고리즘 문제 풀기 2
 
2012 Ds B2 02
2012 Ds B2 022012 Ds B2 02
2012 Ds B2 02
 
2012 Ds B2 02 Pdf
2012 Ds B2 02 Pdf2012 Ds B2 02 Pdf
2012 Ds B2 02 Pdf
 
[SOPT] 데이터 구조 및 알고리즘 스터디 - #01 : 개요, 점근적 복잡도, 배열, 연결리스트
[SOPT] 데이터 구조 및 알고리즘 스터디 - #01 : 개요, 점근적 복잡도, 배열, 연결리스트[SOPT] 데이터 구조 및 알고리즘 스터디 - #01 : 개요, 점근적 복잡도, 배열, 연결리스트
[SOPT] 데이터 구조 및 알고리즘 스터디 - #01 : 개요, 점근적 복잡도, 배열, 연결리스트
 
R_datamining
R_dataminingR_datamining
R_datamining
 
자료구조02
자료구조02자료구조02
자료구조02
 
Probabilistic data structures
Probabilistic data structuresProbabilistic data structures
Probabilistic data structures
 

퍼시스턴트 세그먼트 트리 - Sogang ICPC Team, 2020 Winter

  • 1. #4— 퍼시스턴트세그먼트트리 Persistent Segment Tree by Suhyun Park Sogang ICPC Team – 2020 겨울 고급 스터디 acm.sogang.ac.kr
  • 2. Remarks on Segment Trees Sogang ICPC Team 1 1–10 1–5 6–10 1–3 4–5 6–8 9–10 1–2 3 4 5 6–7 8 9 10 1 2 6 7 f (x, s, e, · · · ) = f ( 2x, s, ⌊ s + e 2 ⌋ , · · · ) + f ( 2x + 1, ⌊ s + e 2 ⌋ + 1, e, · · · )
  • 3. Persistent Segment Tree Sogang ICPC Team 2 T1 1–5 1–3 4–5 1–2 3 4 5 1 2 구조체와 포인터로 구현하며, 2D 쿼리를 처리하기 위해 여러 버전의 트리를 만들 예정 예를 들어 Tn 은 y 좌표가 0 · · · n인 쿼리의 정보를 담는다고 하자
  • 4. Persistent Segment Tree Sogang ICPC Team 3 T0 T1 1–5 1–3 4–5 1–2 3 4 5 1 2 1–5 1–3 4–5 1–2 3 T2 는 T1 을 기반으로 다른 노드들만 새로 만들고, 나머지 노드들은 포인터로 전 버전의 트리를 가리키게 한다, 이를 반복해 트리 생성
  • 5. Persistent Segment Tree Sogang ICPC Team 4 총 데이터 수를 n개라 할 때· · · ✓ 트리 하나 만드는 시간: O (log xmax) ✓ 트리 전체를 만드는 시간: O (n log xmax) ✓ Ti 를 쿼리하는 시간: O (log xmax)
  • 6. Persistent Segment Tree Sogang ICPC Team 5 R = [xl, xr] × [yl, yr] 쿼리하기? ✓ Tn 은 y 좌표가 0 · · · n인 쿼리의 정보를 담는다 ✓ 따라서 R을 쿼리하려면 – Tyr 에 대해서 [xl, xr]을 쿼리 – Tyl−1 에 대해서 [xl, xr]을 쿼리 – 한 후 둘의 차를 구하면 된다 Ti 에 대해 두 번 쿼리하므로 직사각형 영역의 쿼리가 겨우 O (log mx)이다!
  • 7. PSTs vs 2D Segment Trees Sogang ICPC Team 6 0 ≤ x ≤ mx, 0 ≤ y ≤ my 이고 총 데이터 수를 n개라 할 때· · · PST 2D 공간 O (n log mx) O (mxmy) 쿼리 O (log n) O (log mx log my)
  • 8. PSTs vs 2D Segment Trees Sogang ICPC Team 7 하지만 ✓ 값 업데이트 ✓ RMQ 처리 같은 일들은 PST가 할 수 없는 부분이고, 값들이 sparse하지 않으면 의미가 별로 없으므로 PST와 2D segment tree 중 적절한 자료구조를 잘 선택해야 한다
  • 9. PSTs vs 2D Segment Trees Sogang ICPC Team 8 참고: 2D segment tree는 2차원 배열을 이용해 y좌표로 분할 정복 → x좌표로 분할 정복 같은 식으로 만들 수 있다 구현이 생각보다 어렵지 않으니 직접 해 봐도 좋음! (lazy propagation 등 모두 1D segment tree와 비슷하게 처리하면 된다) 2D segment tree source code
  • 10. Egg # 11012 Sogang ICPC Team 9 당신은 임기가 m일 남은 대통령이고, 매일 유세하러 나간다. 유세는 직사각형 모양의 공간을 돌아다니며 진행한다. 시민들은 집에서 당신이 보이면 달걀을 던진다. 시민들의 집의 좌표들이 주어졌을 때, 유세하러 나가면 몇 개의 달걀을 맞을지 구하라. ✓ 집 개수 = n ≤ 10000, m ≤ 50000 ✓ 0 ≤ x, y ≤ 105 ✓ 테스트 케이스 20개
  • 11. Egg # 11012 Sogang ICPC Team 10 문제를 간단히 하면 ✓ R = [xl, xr] × [yl, yr] 내부의 점의 개수를 세는 쿼리를 구현해라 가 된다
  • 12. Egg # 11012 Sogang ICPC Team 11 15 struct pst { 16 struct pst_node { 17 int l, r; 18 int sum; 19 20 pst_node() : l(-1), r(-1), sum(0) {} 21 22 pst_node(int val) : l(-1), r(-1), sum(val) {} 23 24 pst_node(int l, int r, int sum) : l(l), r(r), sum(sum) {} 25 }; 26 27 int xn; 28 vector<pst_node> nodes; 29 vector<int> root_indexes; source code 그냥 세그먼트 트리 만들듯이
  • 13. Egg # 11012 Sogang ICPC Team 12 35 int init(int s, int e) { 36 if (s == e) { 37 nodes.emplace_back(0); 38 } else { 39 int m = (s + e) / 2; 40 int l = init(s, m), r = init(m + 1, e); 41 nodes.emplace_back(l, r, 0); 42 } 43 return nodes.size() - 1; 44 } source code 그냥 세그먼트 트리 만들듯이
  • 14. Egg # 11012 Sogang ICPC Team 13 46 int _sum(int x, int s, int e, int l, int r) { 47 pst_node u = nodes[x]; 48 if (l > r) return 0; 49 if (e < l || r < s) return 0; 50 if (l <= s && e <= r) return u.sum; 51 int m = (s + e) / 2; 52 return _sum(u.l, s, m, l, r) 53 + _sum(u.r, m + 1, e, l, r); 54 } source code 그냥 세그먼트 트리 만들듯이
  • 15. Egg # 11012 Sogang ICPC Team 14 56 int _add(int x, int s, int e, int i, int dv) { 57 pst_node u = nodes[x]; 58 if (s == e) { 59 nodes.emplace_back(u.sum + dv); 60 } else { 61 int m = (s + e) / 2; 62 if (i <= m) { 63 int l = _add(u.l, s, m, i, dv); 64 nodes.emplace_back(l, u.r, u.sum + dv); 65 } else { 66 int r = _add(u.r, m + 1, e, i, dv); 67 nodes.emplace_back(u.l, r, u.sum + dv); 68 } 69 } 70 return nodes.size() - 1; 71 } source code 여기서는 갱신되는 부분만 새로 만들어준다
  • 16. Egg # 11012 Sogang ICPC Team 15 86 void add_back() { 87 root_indexes.emplace_back(root_indexes.back()); 88 } source code 트리 버전 업 – root_indexes에는 버전마다 루트의 인덱스들이 들어 있음
  • 17. Egg # 11012 Sogang ICPC Team 16 101 priority_queue<pii, vector<pii>, greater<>> coords; 102 while (n--) { 103 int x, y; 104 cin >> x >> y; 105 coords.emplace(x, y); 106 } source code 좌표 입력
  • 18. Egg # 11012 Sogang ICPC Team 17 108 pst tree(100000); 109 for (int i = 0; i <= 100000; i++) { 110 while (coords.size() && coords.top().first == i) { 111 tree.update_back(coords.top().second, 1); 112 coords.pop(); 113 } 114 tree.add_back(); 115 } source code PST 생성
  • 19. Egg # 11012 Sogang ICPC Team 18 117 int s = 0; 118 while (q--) { 119 int l, r, b, t; 120 cin >> l >> r >> b >> t; 121 122 s += tree.sum(b, t, l, r); 123 } 124 cout << s << 'n'; source code 쿼리 처리
  • 20. Egg # 11012 Sogang ICPC Team 19 73 int sum(int xl, int xr, int y) { 74 return _sum(root_indexes[y], 0, xn, xl, xr); 75 } 76 77 int sum(int xl, int xr, int yl, int yr) { 78 if (yl <= 0) return sum(xl, xr, yr); 79 return sum(xl, xr, yr) - sum(xl, xr, yl - 1); 80 } source code 쿼리 처리
  • 21. PST로 더 할 수 있는 것 Sogang ICPC Team 20 ✓ 구간에서 k번째 원소 구하기 – 원소의 값들을 좌표 압축하고 구간 내에서 이분 탐색 ✓ 물론 이 트리도 HLD와 섞을 수 있다
  • 22. K번째 수 # 7469 Sogang ICPC Team 21 ✓ Q (i, j, k): 배열 a [i · · · j]를 정렬했을 때, k번째 수를 리턴하는 함수 를 구현해라
  • 23. K번째 수 # 7469 Sogang ICPC Team 22 Q (i, j, k): 배열 a [i · · · j]를 정렬했을 때, k번째 수를 리턴하는 함수 ✓ 배열에 저장된 정수의 값들을 좌표 압축 하듯이 압축하고 – zip : a [i] = v → v′ ✓ 인덱스를 i, 압축된 값을 v′ 라고 했을 때 배열의 원소 각각을 ( v′ , i ) 라는 점들로 생각한다면 ✓ [0, x] × [i, j]에 속한 점의 개수가 k개가 되는 x를 찾으면 된다
  • 24. K번째 수 # 7469 Sogang ICPC Team 23 [0, x] × [i, j]에 속한 점의 개수가 k개가 되는 x를 찾으면 된다 ✓ PST의 경우 기본적으로 [xa, xb] × [0, y]의 쿼리를 처리하므로 ✓ ( [0, x] × [0, j] 에 속한 점의 개수 ) − ( [0, x] × [0, i − 1] 에 속한 점의 개수 ) 를 계산 – 인덱스 트리 쓰듯이 – 쿼리 한 번에 O (log xmax)
  • 25. K번째 수 # 7469 Sogang ICPC Team 24 82 int _kth(int yli, int yri, int s, int e, int k) { 83 if (s == e) return s; 84 int p = nodes[nodes[yri].l].sum - nodes[nodes[yli].l].sum; 85 if (k < p) { 86 return _kth(nodes[yli].l, nodes[yri].l, s, (s + e) / 2, k); 87 } else { 88 return _kth(nodes[yli].r, nodes[yri].r, (s + e) / 2 + 1, e, k - p); 89 } 90 } 91 92 int kth(int yl, int yr, int k) { 93 return _kth(root_indexes[yl - 1], root_indexes[yr], 0, xn, k); 94 } source code
  • 26. K번째 수 # 7469 Sogang ICPC Team 25 116 for (int i = 0; i < n; i++) { 117 cin >> a[i]; 118 zip[a[i]] = 1; 119 } 120 121 int c = 1; 122 for (const auto& kv : zip) { 123 int i = kv.first; 124 zip[i] = c; 125 unzip[c] = i; 126 c++; 127 } source code
  • 27. K번째 수 # 7469 Sogang ICPC Team 26 129 pst tree(100000); 130 131 for (int i = 0; i < n; i++) { // (x, y) = (v', i) 132 tree.add_back(); 133 tree.update_back(zip[a[i]], 1); 134 } source code ( v′ , i ) 임에 주의
  • 28. K번째 수 # 7469 Sogang ICPC Team 27 136 while (m--) { 137 int i, j, k; 138 cin >> i >> j >> k; 139 cout << unzip[tree.kth(i, j, k - 1)] << 'n'; 140 } source code
  • 29. 4회차 연습문제 Sogang ICPC Team 28 ✓ Egg # 11012 ✓ K번째 수 # 7469 1. 수열과 쿼리 22 # 16978 2. 트리와 쿼리 8 # 13517 = 트리와 K번째 수 # 11932 3. XOR 쿼리 # 13538 4. 히스토그램에서 가장 큰 직사각형과 쿼리 # 16977