"R을 이용한 데이터 처리 & 분석 실무 - 서민구 지음" 정리 자료 #1
- https://thebook.io/006723/
- 첫번째 : goo.gl/FJjOlq
- 두번째 : goo.gl/Wdb90g
- 세번째 : goo.gl/80VGcn
- 네번째 : goo.gl/lblUsR
3. 환영합니다!
Sogang ICPC Team 2
저는...
✓ 박수현 – @shiftpsh / shiftpsh.com
✓ Sogang ICPC Team 전임 학회장 – 2019년
✓ ICPC 2019 Seoul Regional에 팀 Redshift로 참가 – 8위
✓ solved.ac를 만든 사람!
4. “고급” 스터디를 시작하기 전에...
Sogang ICPC Team 3
“고급” 스터디는 초급이나 중급 스터디와는 진행방식이 좀 다릅니다
✓ 옆 동네에서 하는 팀 빌딩이나 모의고사 등 재밌는 것들 → 없어요
✓ 기본적으로 혼자 열심히 하셔야 해요
– 여기 오실 정도의 여러분들이 PS에 미친 분들이라는 건 확실해 보이니 걱정하지
않겠습니다
5. “고급” 스터디를 시작하기 전에...
Sogang ICPC Team 4
다루게 되는 것들
✓ solved.ac 기준으로 에서 사이의 문제들
✓ 실무에서는 전혀 쓸 것 같지 않고 실제로도 별로 안 쓰이는 생소한 알고리즘과
자료구조들
– 하지만 대회에서는 나온다
✓ 여하튼 ICPC 등의 “대회 준비”에 초점이 맞춰져 있습니다
6. “고급” 스터디를 시작하기 전에...
Sogang ICPC Team 5
“고급”에 붙어 있는 따옴표는 뭐에요?
✓ 고급 스터디에서 다루는 자료구조와 알고리즘들은 대회 준비의 필요조건이지
충분조건이 아닙니다
– 이런 거 많이 아는 것보다 이미 아는 알고리즘을 잘 써먹는 방법을 공부하는 게
대회 성적에 훨씬 도움이 됩니다
✓ 진짜 “고급” – 이미 알고 있는 알고리즘들을 잘 써먹는 능력을 기르는 스터디
– 팀 연습 등
7. “고급” 스터디를 시작하기 전에...
Sogang ICPC Team 6
그럼 이거 왜 하는 거에요?
✓ 모르면 아예 못 푸는 내용들을 대비하기 위해
✓ 남들한테는 다 웰노운인데 나한테만 아닌 것들을 나에게도 웰노운으로 만들어 써먹기
위해
– 팀노트에 적어가세요
✓ solved.ac 경험치 올리기 위해
8. Remarks on Segment Trees
Sogang ICPC Team 7
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, · · ·
)
9. Remarks on Segment Trees
Sogang ICPC Team 8
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
A5 · · · A9 의 합?
10. Remarks on Segment Trees
Sogang ICPC Team 9
✓ Al · · · Ar 의 합 쿼리 = O (log n)
✓ Ai 업데이트 = O (log n)
11. Lazy Propagation
Sogang ICPC Team 10
✓ Al · · · Ar 의 합 쿼리
✓ Al · · · Ar 업데이트
의 두 쿼리를 처리해야 한다고 생각해 보자
– 하나하나 업데이트한다면 쿼리 한 번에 O ((r − l) log n) → 사실상 O (n log n)
12. Lazy Propagation
Sogang ICPC Team 11
쿼리 한 번에 O (n log n)은 느려도 너무 느리다
→ 업데이트할 때 매번 리프 노드까지 보내지 말자
→ 트리를 하나 더 만들어서 거기다 업데이트하고 쿼리할 때 한꺼번에 업데이트하자
13. Lazy Propagation
Sogang ICPC Team 12
40 ll a[1000001], tree[1048576 * 2], lazy[1048576 * 2];
41
42 void _propagate(int x, int s, int e) {
43 if (!lazy[x]) return;
44 tree[x] += (e - s + 1) * lazy[x];
45 if (s != e) {
46 lazy[x * 2] += lazy[x];
47 lazy[x * 2 + 1] += lazy[x];
48 }
49 lazy[x] = 0;
50 }
lazy라는 이름으로 별도의 트리를 만든다
14. Lazy Propagation
Sogang ICPC Team 13
40 ll a[1000001], tree[1048576 * 2], lazy[1048576 * 2];
41
42 void _propagate(int x, int s, int e) {
43 if (!lazy[x]) return;
44 tree[x] += (e - s + 1) * lazy[x];
45 if (s != e) {
46 lazy[x * 2] += lazy[x];
47 lazy[x * 2 + 1] += lazy[x];
48 }
49 lazy[x] = 0;
50 }
노드 x에 아직 업데이트하지 않은 값이 있다면 트리에 업데이트해주는 메서드
15. Lazy Propagation
Sogang ICPC Team 14
52 ll _sum(int x, int s, int e, int l, int r) {
53 _propagate(x, s, e);
54 if (l > e || r < s) return 0;
55 if (l <= s && e <= r) return tree[x];
56 int m = (s + e) / 2;
57 return _sum(x * 2 , s, m, l, r) +
58 _sum(x * 2 + 1, m + 1, e, l, r);
59 }
합 쿼리를 계산할 때 propagation을 해 줘야 한다
16. Lazy Propagation
Sogang ICPC Team 15
61 void _update(int x, int s, int e, int l, int r, ll dv) {
62 _propagate(x, s, e);
63 if (l > e || r < s) return;
64 if (l <= s && e <= r) {
65 tree[x] += (e - s + 1) * dv;
66 if (s != e) {
67 lazy[x * 2] += dv;
68 lazy[x * 2 + 1] += dv;
69 }
70 return;
71 }
72 int m = (s + e) / 2;
73 _update(x * 2 , s, m, l, r, dv);
74 _update(x * 2 + 1, m + 1, e, l, r, dv);
75 tree[x] = tree[x * 2] + tree[x * 2 + 1];
76 }
업데이트할 때도 propagation을 해 줘야 한다
17. Lazy Propagation
Sogang ICPC Team 16
61 void _update(int x, int s, int e, int l, int r, ll dv) {
62 _propagate(x, s, e);
63 if (l > e || r < s) return;
64 if (l <= s && e <= r) {
65 tree[x] += (e - s + 1) * dv;
66 if (s != e) {
67 lazy[x * 2] += dv;
68 lazy[x * 2 + 1] += dv;
69 }
70 return;
71 }
72 int m = (s + e) / 2;
73 _update(x * 2 , s, m, l, r, dv);
74 _update(x * 2 + 1, m + 1, e, l, r, dv);
75 tree[x] = tree[x * 2] + tree[x * 2 + 1];
76 }
현재 확인하는 구간이 업데이트 구간 안에 있다면 리프 노드까지 가지 않고 lazy를
업데이트하고 끝낸다
19. 구간 합 구하기 2 #
10999
Sogang ICPC Team 18
✓ n ≤ 106
✓ m + k ≤ 20000
구현해보자
20. 구간 합 구하기 2 #
10999
Sogang ICPC Team 19
115 while (q--) {
116 int op, l, r;
117 cin >> op >> l >> r;
118 if (op == 1) {
119 ll dv;
120 cin >> dv;
121 update(l, r, dv);
122 } else { // op == 2
123 cout << sum(l, r) << 'n';
124 }
125 }
source code
21. JuQueen #
10277
Sogang ICPC Team 20
코어가 4, 587, 520개인 컴퓨터의 클럭을 변경하는 프로그램을 짜야 한다. N 단계의 클럭이
가능하고
✓ CPU x의 클럭을 s단계만큼 증감
✓ CPU a · · · b의 클럭을 s단계만큼 증감
✓ CPU x의 클럭 단계 출력
중 하나를 할 수 있다.
클럭은 한 단계씩만 올라가거나 내려가며, 여러 코어에 대해 여러 단계를 한꺼번에 증감할
경우 그 중 하나라도 0단계나 N 단계에 도달하면 증감을 종료하고 증감한 만큼의 단계를
출력한다.
22. JuQueen #
10277
Sogang ICPC Team 21
클럭은 한 단계씩만 올라가거나 내려가며, 여러 코어에 대해 여러 단계를 한꺼번에 증감할
경우 그 중 하나라도 0단계나 N 단계에 도달하면 증감을 종료하고 증감한 만큼의 단계를
출력한다.
✓ 지금 클럭들이 1, 2, 3, 4고 N = 6이라면, 클럭 4단계를 올리라는 명령을 내린다면 2
단계만 올라가고 끝난다
23. JuQueen #
10277
Sogang ICPC Team 22
CPU a · · · b의 클럭 단계의 최솟값을 mn, 최댓값을 mx라 하고, s만큼 증감하고자 한다면
✓ mx + s > N 이라면? N − mx만큼만 올릴 수 있다
✓ mn + s < 0이라면? mn만큼만 내릴 수 있다
✓ 둘 다 아니라면 s만큼 올릴 수 있다
24. JuQueen #
10277
Sogang ICPC Team 23
세 가지 연산을 할 수 있는 자료구조가 필요
✓ 인덱스 l · · · r에서의 최솟값 구하기
✓ 인덱스 l · · · r에서의 최댓값 구하기
✓ 인덱스 l · · · r을 전부 s만큼 증가시키기
하나의 원소 i에 대해서만 연산할 경우 l = r = i
25. JuQueen #
10277
Sogang ICPC Team 24
A = {a0, a1, · · · , an}
A′
= {a0 + d, a1 + d, · · · , an + d}
이라고 하면
max
a′∈A′
(
a′
)
= max
a∈A
(a) + d
min
a′∈A′
(
a′
)
= min
a∈A
(a) + d
이 성립 → 그냥 더해주면 된다. 별 문제 없다
26. JuQueen #
10277
Sogang ICPC Team 25
✓ 인덱스 l · · · r에서의 최솟값 구하기
✓ 인덱스 l · · · r에서의 최댓값 구하기
· · · 를 하기 위해 세그먼트 트리를 튜플 ⟨min, max⟩ 로 만들고
✓ 인덱스 l · · · r을 전부 s만큼 증가시키기
· · · 를 하기 위해 lazy propagate 하면 되겠다!
27. JuQueen #
10277
Sogang ICPC Team 26
40 struct mii {
41 ll mn, mx;
42 };
43
44 mii tree[8388608 * 2];
45 int lazy[8388608 * 2];
46
47 int inf = 987654321;
48 int c, n, o;
source code
first, second는 헷갈리니까 struct를 새로 만든다. 어차피 정렬할 것도 아닌데· · ·
28. JuQueen #
10277
Sogang ICPC Team 27
50 void _propagate(int x, int s, int e) {
51 if (!lazy[x]) return;
52 tree[x].mn += lazy[x];
53 tree[x].mx += lazy[x];
54 if (s != e) {
55 lazy[x * 2] += lazy[x];
56 lazy[x * 2 + 1] += lazy[x];
57 }
58 lazy[x] = 0;
59 }
source code
Propagation – 더해주는 쿼리를 처리해 주기 위함
29. JuQueen #
10277
Sogang ICPC Team 28
61 ll _minimum(int x, int s, int e, int l, int r) {
62 _propagate(x, s, e);
63 if (l > e || r < s) return inf;
64 if (l <= s && e <= r) return tree[x].mn;
65 int m = (s + e) / 2;
66 return min(_minimum(x * 2, s, m, l, r),
67 _minimum(x * 2 + 1, m + 1, e, l, r));
68 }
source code
구간 최솟값 쿼리를 처리하면서 propagation도 같이 한다
30. JuQueen #
10277
Sogang ICPC Team 29
70 ll _maximum(int x, int s, int e, int l, int r) {
71 _propagate(x, s, e);
72 if (l > e || r < s) return 0;
73 if (l <= s && e <= r) return tree[x].mx;
74 int m = (s + e) / 2;
75 return max(_maximum(x * 2, s, m, l, r),
76 _maximum(x * 2 + 1, m + 1, e, l, r));
77 }
source code
최댓값에 대해서도 동일하게 처리해 준다
31. JuQueen #
10277
Sogang ICPC Team 30
79 void _update(int x, int s, int e, int l, int r, ll dv) {
80 _propagate(x, s, e);
81 if (l > e || r < s) return;
82 if (l <= s && e <= r) {
83 tree[x].mn += dv;
84 tree[x].mx += dv;
85 if (s != e) {
86 lazy[x * 2] += dv;
87 lazy[x * 2 + 1] += dv;
88 }
89 return;
90 }
91 int m = (s + e) / 2;
92 _update(x * 2 , s, m, l, r, dv);
93 _update(x * 2 + 1, m + 1, e, l, r, dv);
94 tree[x].mn = min(tree[x * 2].mn, tree[x * 2 + 1].mn);
95 tree[x].mx = max(tree[x * 2].mx, tree[x * 2 + 1].mx);
96 }
source code
32. JuQueen #
10277
Sogang ICPC Team 31
98 ll minimum(int l, int r) {
99 return _minimum(1, 0, c - 1, l, r);
100 }
101
102 ll maximum(int l, int r) {
103 return _maximum(1, 0, c - 1, l, r);
104 }
105
106 void update(int l, int r, ll dv) {
107 _update(1, 0, c - 1, l, r, dv);
108 }
source code
함수 인자가 너무 많으면 헷갈리므로 개인적으로는 첫 호출을 하는 함수를 따로 만들어서
사용하는 중
33. JuQueen #
10277
Sogang ICPC Team 32
118 string op;
119 cin >> op;
120 if (op[0] == 's') { // ”state”
121 int x;
122 cin >> x;
123 cout << minimum(x, x) << 'n';
124 } else if (op[0] == 'g') { // ”groupchange”
source code
값을 구하는 쿼리는 따로 만들 필요 없이 최솟값이나 최댓값 쿼리로 해결 가능하다
34. JuQueen #
10277
Sogang ICPC Team 33
124 } else if (op[0] == 'g') { // ”groupchange”
125 int a, b, s;
126 cin >> a >> b >> s;
127
128 ll mn = minimum(a, b), mx = maximum(a, b);
129 if (s > 0 && s + mx > n) {
130 update(a, b, n - mx);
131 cout << n - mx << 'n';
132 } else if (s < 0 && s + mn < 0) {
133 update(a, b, -mn);
134 cout << -mn << 'n';
135 } else {
136 update(a, b, s);
137 cout << s << 'n';
138 }
139 } else { // ”change”
source code
구간 최대/최소를 구해서 증감할 수 있는 만큼 증감해 주고 결과를 출력한다
35. JuQueen #
10277
Sogang ICPC Team 34
139 } else { // ”change”
140 int x, s;
141 cin >> x >> s;
142
143 ll val = minimum(x, x);
144 if (s > 0 && s + val > n) {
145 update(x, x, n - val);
146 cout << n - val << 'n';
147 } else if (s < 0 && s + val < 0) {
148 update(x, x, -val);
149 cout << -val << 'n';
150 } else {
151 update(x, x, s);
152 cout << s << 'n';
153 }
154 }
155 }
source code
마찬가지.
36. Lazy Propagation Not on Segment Trees
Sogang ICPC Team 35
Lazy propagation의 아이디어는 세그먼트 트리에 한정해서 쓰이지 않는다
37. Pixel Triangles #
16572
Sogang ICPC Team 36
2, 000 × 2, 000 크기의 격자 판이 주어지며, 각 픽셀(칸)은 행과 열을 이용해서 위치가
표시된다. 가장 왼쪽 열이 1열, 가장 위쪽 행이 1행이다.
이 때 픽셀 삼각형의 정의는 다음과 같다.
✓ 픽셀 삼각형은 3개의 자연수 A, B, C 에 대해서 P (A, B, C)로 표현된다.
✓ P (A, B, C) = {(x, y) | A ≤ x, B ≤ y, 0 ≤ (x − A) + (y − B) ≤ C − 1}
격자 위의 픽셀들로 구성된 픽셀 삼각형이 n ≤ 4, 000, 000개 주어졌을 때, 픽셀 삼각형들이
덮는 픽셀의 개수를 출력해라.