This repository has been archived by the owner on Nov 21, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdescription.typ
504 lines (465 loc) · 28.6 KB
/
description.typ
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
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
#import "colors.typ" : KUPC_GREEN, PALE_RED
#import "problem_info.typ" : constructTitle
#import "problems.typ" : contest_problems
#import "emoji/lib.typ" : *
#let mono(s, color: black) = {text(font: "Inconsolata", fill: color)[#s]}
#let bf(s) = {text(weight: "bold")[#s]}
// 줄바꿈은 #linebreak()를 중간에 넣으면 됩니다.
// 페이지 넘김은 문제 내부에서 ()를 새로 만들어 주세요.
// monospace 문자열은 #mono("abc")와 같이 쓸 수 있습니다.
// 미리 정의되지 않은 operation의 경우에는 #math.op("MEX")와 같이 쓰면 됩니다. 대부분은 정의돼 있으니 그냥 $cos$ 와 같이 쓰면 됩니다.
// 시간복잡도는 $cal(O)(N log N)$ 와 같이 써주세요. log가 자체적으로 함수가 있습니다.
// 이모지는 그냥 넣으면 됩니다. 지원하지 않는 이모지는 깨집니다. 이모지는 https://github.com/polazarus/typst-svg-emoji 를 사용했습니다.
// 그래프도 작성 가능합니다. https://www.graphviz.org/docs/graph/ 를 참고해서 ```render <여기에 그래프를 작성하세요>```
#let cell(num, color: black, fill: none, stroke: none) = {
rect(
height: 50pt, width: 50pt,
stroke: stroke,
fill: fill,
)[#align(center + horizon)[#text(fill: color)[#num]]];
}
#let descriptions = (
// 2A
(
(
[- 주어진 문자열에서 $1$이 연속으로 등장하는 구간을 알아내는 방법은 무엇일까요?],
[- 여러 가지 방법이 있지만, 구현이 간단한 방법을 소개합니다.],
[- #mono(1) 이후에 #mono(0) 이 등장한다면, 검은 줄의 구간이 끝나는 것을 의미합니다.],
[- 주어진 문자열을 훑으면서, #mono("s[i] = 1")이면서 #mono("s[i+1] = 0")인 #mono("i")의 개수를 셉시다.],
[- 편의를 위해, 주어진 문자열에 #mono(0) 을 추가해도 답은 같습니다.],
[- 시간복잡도는 $cal(O)(N)$ 입니다.],
),
),
// 2B/1A
(
(
[- 건덕이와 건구스는 자신의 차례에 #bf("반드시") 움직여야 합니다.],
[- 건덕이와 건구스는 다음과 같은 네 가지 종류로 움직입니다: #mono("RR"), #mono("RL"), #mono("LL"), #mono("LR")],
[- 어떻게 움직이더라도 두 플레이어 사이 간격의 #bf("홀짝성")#sub(mono("parity"))은 변하지 않습니다.],
),
(
[- 전장의 길이가 주어지는 순간 승자는 결정되며, 승자는 상대를 향해 전진합니다.],
[- 패자의 경우 앞으로 가면 패배로 더 빠르게 도달하므로, 가능하다면 뒤로 후퇴합니다.],
[- 전장을 벗어나도록 이동할 수 없으므로, #bf("두 플레이어간 간격은 단조감소")합니다.],
),
(
[- 간격을 짝수로 만드는 사람은 결국 두 플레이어 사이의 거리를 $0$으로 만듭니다.],
[- 플레이어 사이의 거리가 $0$이 되는 순간, 다음 차례의 플레이어는 공격할 수 있습니다.],
[- 주어진 전장의 길이가 짝수라면, 선공이 먼저 둘 사이의 간격을 홀수로 만듭니다.],
[- 홀수라면, 선공이 둘 사이의 간격을 짝수로 만듭니다.],
[- 따라서 전장의 길이가 짝수인 경우, 건덕이가 승리합니다.],
[- 주어진 전장의 길이의 홀짝성을 판단하므로 시간복잡도는 $cal(O)(1)$입니다.],
[],[],
[#emoji.arm.muscle $N times N$ 전장에서 각 플레이어가 $(1,1)$, $(N,N)$에서 시작하면 누가 승리할까요?]
),
),
// 2C
(
(
[- 바닥수 $N$이 주어졌을 때, 바닥수가 $N$인 길이 $L$의 원래 수를 구해야 합니다.],
[- 길이 $L$의 모든 수에 대해서 바닥수가 되는지 확인하는 것은 오래 걸립니다.],
[- 곱셈의 성질을 활용해서 쉽게 문제를 해결해 봅시다.]
),
(
[- 곱셈의 항등원을 이용합시다. $1$은 여러 번 곱해도 $1$입니다.],
[- $1$을 $L-1$번 적은 뒤, $N$를 뒤에 덧붙인 수의 바닥수는 $N$입니다.],
[- 원래 수는 $0$으로 시작하지 않음에 주의합시다.]
),
),
// 2D/1B
(
(
[- 학생의 키가 같은 쌍이 존재하지 않는다면, 모든 학생이 참여할 수 있습니다.],
[- 모두가 한 쪽을 바라보고, 바라보는 방향으로 키가 작아지도록 줄세우면 됩니다.],
[- 학생의 키가 같은 쌍이 존재하는 경우에는 몇 명까지 참여할 수 있을까요?],
),
(
[- 두 명의 키가 서로 같다면, 서로 반대 방향을 보고 서면 됩니다.],
[- 세 명의 키가 서로 같다면, 한 명은 어느 쪽을 보든 점프할 타이밍을 놓칠 수 있습니다.],
[- 따라서 키가 같은 사람은 #bf("최대 두 명")까지만 참여할 수 있습니다.],
[- 줄을 세울 수 있는지의 여부를 물어보았으므로, 각 사람들의 키에 대해서 참여할 수 있는 사람의 수를 세어 줍시다.],
[- 시간복잡도는 $cal(O)(N)$입니다.],
[],[],
[#emoji.arm.muscle 올바르게 줄 세우는 방법 중 하나를 구할 수 있을까요?]
),
),
// 2E
(
(
[- 왼쪽 절반의 문자와 오른쪽 절반의 문자의 교환을 통해 팰린드롬을 만들어 봅시다.],
[- 연산을 통해 임의의 위치에 존재하는 두 문자를 교환할 수 있다면, #linebreak() 모든 문자를 원하는 곳에 배치할 수 있습니다. 과연 가능할까요?#v(0.5em)],
[- 편의상 배열의 길이가 짝수라고 가정합시다. 교환하는 경우의 수는 두 가지입니다. ],
[#h(2em) #emoji.ast 두 문자가 서로 다른 절반에 속하는 경우],
[#h(2em) #emoji.ast 두 문자가 같은 절반에 속하는 경우],
),
(
[- 두 문자가 서로 다른 절반에 속하는 경우, 두 원소를 직접 교환할 수 있습니다.],
[- 두 문자가 서로 같은 절반에 속하는 경우에는 두 원소를 직접 교환할 수 없습니다.],
[- 이때, 반대쪽 절반에 속하는 원소 한 개를 임시로 사용해 교환할 수 있습니다.],
[
#let cell(num, color: black, fill: none) = {
rect(
height: 50pt, width: 50pt,
stroke: none,
fill: fill,
)[#align(center + horizon)[#text(fill: color)[#num]]];
}
#set table(align: center, stroke: gray + 0.5pt, inset: 0pt, columns: 4)
#align(center)[
#table(
)[#cell(1, color: blue, fill:rgb("ddd"))][#cell(2)][#cell(3, color: PALE_RED)][#cell(4, color: PALE_RED, fill: rgb("ddd"))]
]
#v(-0.8em)
#align(center)[
#table(
)[#cell(4, color: PALE_RED, fill:rgb("ddd"))][#cell(2)][#cell(3, color: PALE_RED, fill:rgb("ddd"))][#cell(1, color: blue)]
]
#v(-0.8em)
#align(center)[
#table(
)[#cell(3, color: PALE_RED, fill:rgb("ddd"))][#cell(2)][#cell(4, color: PALE_RED)][#cell(1, color: blue, fill:rgb("ddd"))]
]
#v(-0.8em)
#align(center)[
#table(
)[#cell(1, color: blue)][#cell(2)][#cell(4, color: PALE_RED)][#cell(3, color: PALE_RED)]
]
]
),
(
[- 문자열의 길이가 홀수인 경우에는 가운데 글자를 교환할 수 없음에 유의합시다.],
[- 해당 문자를 세지 않고, 짝수 길이의 문자열이라고 생각하면 됩니다.],
[- 팰린드롬이 되기 위해서는 양 쪽에 같은 수의 문자가 존재해야 합니다.],
[- 알파벳이 문자열에 짝수번 등장한다면 양 쪽에 골고루 분배할 수 있습니다.],
[- 시간복잡도는 $cal(O)(N)$입니다.],
),
),
(
(
[- 두 개의 말뚝과 깃대 하나를 골라 만들 수 있는 삼각형 넓이 중 최댓값을 구합시다.],
[- 단, 넓이가 $R$ 이하여야 합니다.],
[- 간단한 방법은 모든 말뚝 쌍에 대해서, 모든 깃대를 탐색하는 방법입니다.],
[- 해당 풀이는 $cal(O)(N^2M)$으로, 제한 시간 안에 해결할 수 없습니다.],
),
(
[- 만약 밑변을 고정한다면, 주어진 깃대를 정렬한 뒤 이분탐색을 사용할 수 있습니다.],
[- 가능한 모든 밑변을 구하는 데에는 $cal(O)(N^2)$입니다.],
[- 이분 탐색에 깃대를 활용하기 위해 깃대를 정렬하는 데 $cal(O)(M log M)$입니다.],
[- 하나의 밑변 길이에 대해서, $R$ 이하인 최대 넓이를 구하는 데 $cal(O)(log M)$이 듭니다.],
[- $cal(O)(M log M + N^2 log M)$ 은 충분히 통과할 수 있습니다.]
),
),
(
(
[- 스위치를 누르면 $3$초동안 두 배의 점수를 얻을 수 있습니다.],
[- 음수의 점수도 두 배로 계산됨에 유의해야 합니다.],
[- 그리디하게 스위치를 누르는 전략은 오답을 받습니다.],
[- 스위치를 누른 뒤, $i$초 시점에 스위치의 효과가 $j$초 남았다는 것을 활용합시다.],
),
(
[- $i$초에 $j=0$이라면, $i-1$초에 $j=0$이거나 $j=1$인 경우 중 최댓값을 가져옵시다.],
[- $j=1$이라면 아직 스위치의 효과가 남아있으므로 $i-1$초의 $j=2$를 가져옵니다.],
[- $j=2$인 경우도 마찬가지로 $i-1$초의 $j=3$을 가져옵니다.],
[- $j=3$은 지금 스위치를 누른 상황으로, $i-1$초의 $j=0$이거나 $j=1$인 경우에서 가져올 수 있습니다.],
[- 스위치의 효과가 적용되는 경우에는 배열의 값의 두배를 더해줍시다.],
[- 시간복잡도는 $cal(O)(N)$입니다.]
)
),
(
(
[ #let cell(num, color: black, fill: none, stroke: none) = {
rect(
height: 50pt, width: 50pt,
stroke: stroke,
fill: fill,
)[#align(center + horizon)[#text(fill: color)[#num]]];
}
#set table(align: center, stroke: rgb("add8e6") + 1pt, inset: 0pt, columns: 5)
#align(center)[
#table()[#cell(1)][#cell(2)][#cell(3, stroke: red)][#cell(4)][#cell(5)][#cell(5)][#cell(2)][#cell(1, stroke: red)][#cell(4)][#cell(6)][#cell(0)][#cell(2)][#cell(4,fill: rgb("ddd"), stroke: red)][#cell(2)][#cell(1)][#cell(0)][#cell(0)][#cell(2)][#cell(1)][#cell(7)]
]
],
[- 일감호입니다. #emoji.fishing],
[- 무게추 $3$을 달아 $3$의 힘으로 낚싯대를 휘두르면 회색 칸에 찌가 도달합니다.],
[- 빨간 색 테두리에 해당하는 칸에 존재하는 $8$마리의 물고기가 사로잡힙니다.]
),
(
[ #set table(align: center, stroke: rgb("add8e6") + 1pt, inset: 0pt, columns: 5)
#align(center)[
#table()[#cell(1)][#cell(2, stroke: red)][#cell(3, fill: rgb("add8e6"))][#cell(4)][#cell(5)][#cell(5)][#cell(2, fill: rgb("ddd"), stroke: red)][#cell(1, fill: rgb("add8e6"))][#cell(4)][#cell(6)][#cell(0)][#cell(2)][#cell(4, fill: rgb("add8e6"))][#cell(2)][#cell(1)][#cell(0)][#cell(0)][#cell(2)][#cell(1)][#cell(7)]
]
],
[- 낚싯줄을 한 바퀴 감아올리면 찌의 위치가 바뀝니다.],
[- 바뀐 칸에서 물고기 $4$마리가 사로잡힙니다.],
[- 이를 미끼가 일감호를 벗어날 때까지 반복합니다.],
),
(
[ #let cell(num, color: black, fill: none, stroke: none) = {
rect(
height: 50pt, width: 50pt,
stroke: stroke,
fill: fill,
)[#align(center + horizon)[#text(fill: color)[#num]]];
}
#set table(align: center, stroke: rgb("add8e6") + 1pt, inset: 0pt, columns: 5)
#align(center)[
#table()[#cell(1, fill: rgb("add8e6"))][#cell(2, fill: rgb("add8e6"))][#cell(3, fill: rgb("add8e6"))][#cell(4)][#cell(5)][#cell(5)][#cell(2, fill: rgb("add8e6"))][#cell(1, fill: rgb("add8e6"))][#cell(4)][#cell(6)][#cell(0)][#cell(2)][#cell(4, fill: rgb("add8e6"))][#cell(2)][#cell(1)][#cell(0)][#cell(0)][#cell(2)][#cell(1)][#cell(7)]
]
],
[- 쿼리가 주어질 때마다 다음과 같은 계단 모양에 칠해진 수의 합을 구하는 문제입니다.],
[- 간단하게 생각하면, 매 쿼리마다 계산하는 $cal(O)(N M Q)$ 풀이가 있습니다 #emoji.face.explode.],
[- 이는 시간 초과입니다. 어떻게 빠르게 해결할 수 있을까요?]
),
(
[- 여러 방법 중 하나를 소개합니다.],
[- 2차원 배열에서 직사각형 모양의 부분합은 $cal(O)(N M + Q)$에 구할 수 있습니다.],
[- 쿼리가 직사각형의 부분합을 구하게끔 일감호를 재배치할 수 있을까요?],
),
(
[
#set table(align: center, stroke: rgb("add8e6") + 1pt, inset: 0pt, columns: 3)
#align(center)[
#grid(columns: 3)[
#align(center + horizon)[
#table()[#cell(1, fill: rgb("add8e6"))][#cell(2,fill: rgb("add8e6"))][#cell(3,fill: rgb("add8e6"))][#cell(5)][#cell(2,fill: rgb("add8e6"))][#cell(1,fill: rgb("add8e6"))][#cell(0)][#cell(2)][#cell(4,fill: rgb("add8e6"))]]
][#align(center + horizon)[#h(3em)->#h(3em)]][
#align(center + horizon)[
#table()[#cell(0, fill: rgb("add8e6"))][#cell(0, fill: rgb("add8e6"))][#cell(3, fill: rgb("add8e6"), stroke: PALE_RED)][#cell(0,fill: rgb("add8e6"))][#cell(2,fill: rgb("add8e6"), stroke: PALE_RED)][#cell(1,fill: rgb("add8e6"), stroke: PALE_RED)][#cell(1, fill: rgb("add8e6"), stroke: PALE_RED)][#cell(2, fill: rgb("add8e6"), stroke: PALE_RED)][#cell(4,fill: rgb("add8e6"), stroke: PALE_RED)][#cell(5, stroke: PALE_RED)][#cell(2, stroke: PALE_RED)][#cell(0)][#cell(0, stroke: PALE_RED)][#cell(0)][#cell(0)]]
]
]
],
[- 가능합니다.],
[- 시간복잡도는 2차원 부분합과 같은 $cal(O)(N M + Q)$입니다.],
),
(
[- 아래쪽으로 누적합을 구해준 뒤, 오른쪽 아래 대각선 방향으로 누적합을 구해나가도 정답을 구할 수 있습니다.],
[],[],
[
#let c1 = rgb(240,229,235);
#let c2 = rgb(212,236,220)
#let c3 = rgb(252,249,218)
#let c4 = rgb(252,223,215)
#set table(align: center, stroke: rgb("add8e6") + 1pt, inset: 0pt, columns: 3)
#align(center)[
#grid(columns: 5)[
#align(center + horizon)[
#table()[#cell(1, fill: c1)][#cell(2,fill: c2)][#cell(3,fill: c3)][#cell(5, fill: c1)][#cell(2,fill: c2)][#cell(1,fill: c3)][#cell(0, fill: c1)][#cell(2, fill: c2)][#cell(4,fill: c3)]]
][#align(center + horizon)[#h(1em)->#h(1em)]][#align(center + horizon)[
#table()[#cell(1, fill: c1)][#cell(2,fill:c2)][#cell(3)][#cell(6,fill:c3)][#cell(4,fill: c1)][#cell(4,fill: c2)][#cell(6)][#cell(6, fill:c3)][#cell(8,fill: c1)]]][#align(center + horizon)[#h(1em)->#h(1em)]][
#table()[#cell(1)][#cell(2)][#cell(3)][#cell(6)][#cell(5)][#cell(6)][#cell(6)][#cell(12)][#cell(13)]
]
]
],
),
),
(
(
[- 주어진 등굣길을 그래프로 모델링해 봅시다.],
[- 각 칸에 쓰인 수만큼 양쪽 방향으로 떨어진 칸으로 향하는 간선을 만들 수 있습니다.],
[- 최대 두 번 방향을 반전할 수 있으므로, 같은 칸이라도 $0$번 반전했을 때와 $2$번 반전했을 때는 다르게 생각해야 합니다.],
),
(
[- 정점을 세 개로 나누어 아래와 같은 그래프 모형을 생각할 수 있습니다.],
[#align(center)[#image("images/time.png", width: 70%)]]
),
(
[- 문제의 정답은 $1$번 정점에서 학교에 해당하는 정점들까지의 최장거리와 같습니다.],
[- 구축한 그래프는 사이클 없는 방향 그래프입니다.],
[- $1$번 정점에서 위상정렬을 시행한 뒤, 정렬한 순서대로 정답을 갱신해나가면 됩니다.],
[- 학교에 처음 도착한 시간을 늦추는 것에 유의합시다. 학교에서 되돌아가는 간선은 존재할 수 없습니다.],
[- 시간복잡도는 $cal(O)(N)$입니다.],
),
(
[- 더 쉬운 풀이로, 단순하게 오른쪽으로 한 번, 왼쪽으로 한 번, 다시 오른쪽으로 한 번 DP를 훑어도 해결할 수 있습니다.],
[- 아직 나에게 오는 경로가 없거나 이미 학교에 도착한 경우, 칸에 숫자가 $0$이 쓰인 경우 등을 잘 처리해야 합니다.],
[- 시간복잡도는 위와 같은 $cal(O)(N)$이지만, 상수가 작아 더 빠르게 통과합니다.],
)
),
(
(
[- 주어진 문자열을 하나의 집합으로 생각합니다.],
[- 예를 들어 문자열이 #mono("0011223344")라면 대응되는 집합은 ${0,1,2,3,4}$입니다.],
[- 숫자는 $10$개 이므로 만들어질 수 있는 집합의 개수는 $2^10 = 1024$개입니다.],
[- 이제 집합 두 개를 합쳐 크기가 $K$인 집합의 개수를 세면 됩니다.],
),
(
[- 그럼 크기가 $K$인 집합의 개수는 어떻게 셀까요?],
[- 만들어질 수 있는 $1024$개의 집합 중 $A union B$의 원소의 개수가 $K$개인 두 집합 $A$, $B$를 순서를 고려하지 않고 뽑습니다.],
[- $A=B$일 때는 정답에 $(#math.op("cnt")\(A\) times (#math.op("cnt") (A) - 1))/2$를 더합니다.],
[- $A!=B$일 때는 정답에 $#math.op("cnt") (A) times #math.op("cnt") (B)$를 더합니다.],
[- 시간복잡도는 $cal(O)(N + 1024^2 times 10)$입니다.],
[- 비트집합을 사용하면 $cal(O)(N + 1024^2)$으로 줄일 수 있지만, 사용하지 않아도 됩니다.]
),
),
(
(
[- 우선 언제 #mono("NO", color:PALE_RED) 를 출력해야할 지 생각해봅니다.],
[- #math.op("MEX")의 정의에 의해, 길이가 $N$인 수열의 #math.op("MEX")는 $N+1$보다 클 수 없습니다.],
[- $A$는 순열이기 때문에 $B_N = N+1$입니다.],
[- $S_i = {A_1, A_2, dots, A_i}$라고 할 때, $S_i subset S_(i+1)$이므로 #linebreak() $B_i > B_(i+1) (1 <= i <= N-1)$일 수 없습니다. 즉 $B$는 #bf("단조증가")합니다.],
[- 위 세 가지 규칙을 지키지 않는다면 #mono("NO", color:PALE_RED)를 출력해야 합니다.],
[- 그 이외의 경우에는 모두 가능할까요?],
),
(
[- 몇 가지 관찰을 해 봅시다.],
[- 만약 $A_i = K$라면, $i<=j$일 때 $B_j = K$일 수 없습니다.],
[- $K$를 포함하는 집합의 #math.op("MEX")는 정의상 $K$일 수 없기 때문입니다.],
[- 결국 $B_j = K$일 때, $A_i = K (j<i)$입니다.]
),
(
[- $B_i != B_(i+1) (1 <= i <= N-1)$인 경우에 주목합니다.],
[- #math.op("MEX")값이 $B_(i+1)$이기 위해서는 해당 집합이 $B_i$를 가지고 있어야 합니다.],
[- 이전 관찰과 종합하면 $B_i != B_(i+1)$일 때, $A_(i+1) = B_i$가 됨을 알 수 있습니다.],
),
(
[- 이제 남은 값을 배치하는 방법은 여러 가지가 있습니다.],
[- 그 중 하나는 작은 수부터 빈 곳에 채워 넣는 것입니다.],
[- 왜 이 방법이 가능할까요?],
),
(
[- 채워야하는 곳은 $B_(i-1) = B_i$인 $A_i$입니다.],
[- 해당 방법으로 빈 곳을 배치하면 $B_i < A_i$입니다.],
[- #math.op("MEX")값이 $B_i$인 배열에 $B_i$보다 큰 값을 배치하더라도 #math.op("MEX")값은 변하지 않습니다.],
[- 그리고 $B_(k-1) != B_k$인 $k$에 대해 총 $k$개의 공간에 $B_k$보다 작은 $B_k - 1 (<= k)$개의 수를 배치하므로 작은 수부터 배치하면 원하는 결과를 얻을 수 있습니다.],
),
(
[- $B_i != B_(i+1)$일 때, $A_(i+1) = B_i$ #v(1em)],
[#align(center)[#table(columns:5)[#cell(1)][#cell(2)][#cell(2)][#cell(4)][#cell(6)]]],
[#v(-0.5em) $ B $ ],
[#align(center)[#table(columns:5)[#cell("?")][#cell(1, color:PALE_RED)][#cell("?")][#cell(2, color:PALE_RED)][#cell(4, color:PALE_RED)]]],
[#v(-0.5em) $ A $ ],
),
(
[- 남은 값을 작은 수부터 빈 곳에 채워 넣습니다. #v(1em)],
[#align(center)[#table(columns:5)[#cell(1)][#cell(2)][#cell(2)][#cell(4)][#cell(6)]]],
[#v(-0.5em) $ B $ ],
[#align(center)[#table(columns:5)[#cell(3, color:PALE_RED)][#cell(1)][#cell(5, color:PALE_RED)][#cell(2)][#cell(4)]]],
[#v(-0.5em) $ A $ ],
[- 시간복잡도는 $cal(O)(N)$입니다.],
)
),
(
(
[- 하나의 등불이 다른 등불을 앞지르는 횟수가 곧 소원을 비는 횟수입니다.],
[- 상대적으로 오른쪽의 등불이 상승하는 정도가 더 크면, #linebreak()왼쪽의 등불을 앞지르는 순간이 존재합니다.],
[- 길이가 $S$인 구간에서 $i<j$ 이면서 $A_i < A_j$인 $(i, j)$ 쌍의 개수를 구해야 합니다.],
),
(
[- 구간 내 원소에 대해서 모든 쌍을 찾는 방법을 먼저 생각해 봅시다.],
[- 하나의 구간에 대해 $cal(O)(N^2)$이 걸리므로, 모든 구간에서 계산한다면 $cal(O)(N^3)$으로 제한시간 내에 풀 수 없습니다.],
[- 앞지르는 쌍의 개수를 더 빠르게 구할 수 있을까요?],
),
(
[- Merge Sort를 응용하면 문제를 조금 더 효율적으로 풀 수 있습니다.],
[- 두 배열을 합치는 과정에서, 양쪽 배열은 정렬된 상태입니다.],
[- 왼쪽 절반 배열의 원소가 더 작다면, 해당 원소보다 뒤에 존재하는 원소 모두가 오른쪽에서 고른 원소보다 작습니다.],
[- 하나의 구간에서 모든 쌍의 개수를 $cal(O)(N log N)$에 구할 수 있습니다.],
[- 여전히 모든 구간을 확인하는 데 $cal(O)(N^2 log N)$으로, 더 빠른 방법이 필요합니다.],
),
(
[- 문제의 특성을 파악해서 빠르게 풀어 봅시다.],
[- $k$번 등불부터 $S$개 고른 구간을 $T_k$라고 합시다.],
[- $T_k$ 구간과 $T_(k+1)$ 구간의 공통된 구간은 $[k+1, k+S-1]$입니다.],
[- 공통된 구간에서 발생하는 앞지르는 쌍의 개수는 $k$번째 등불과 $k+S$번째 등불에 영향받지 않습니다.],
[- 해당 구간의 연산을 매 구간마다 반복하지 않으면 효율적으로 풀 수 있습니다.],
),
(
[- $T_k $구간에서 다음 구간으로 넘어갈 때 아래의 값들을 빼고 더해줘야 합니다.],
[#h(2em) #emoji.ast $k$ 번째 등불을 제거했을 때 감소하는 앞지르는 쌍의 개수],
[#h(2em) #emoji.ast $k+S$ 번째 등불을 추가할 때 증가하는 앞지르는 쌍의 개수],
[],
[- 세그먼트 트리를 활용하면 앞지르는 쌍의 개수를 빠르게 구할 수 있습니다.],
[#h(2em)#emoji.ast $k$번째 등불을 제거할 때, $A_k$보다 상승하는 정도가 큰 등불의 개수],
[#h(2em)#emoji.ast $k+S$번째 등불을 추가할 때, $A_(k+S)$보다 상승하는 정도가 작은 등불의 개수]
),
(
[- 주어진 등불 상승 속도의 범위가 최대 $10^9$이므로 좌표압축을 먼저 진행해야 합니다.],
[- 시간복잡도는 $cal(O)(N log N)$입니다.],
),
),
(
(
[- BFS의 순회 순서대로 정점에 번호를 붙이겠습니다. 정점을 적절히 배치해서 순회 순서의 차를 최대화해야 합니다.],
[- 루트 바로 아래에 $k$개의 정점이 존재한다고 합시다.],
[- 트리의 $i(1 <= i <= N)$번 정점을 루트로 하는 서브트리의 크기를 $S_i$라고 합시다.],
[- $sum_(i=2)^(k+1) S_i$ 는 루트 노드를 제외한 트리의 크기이므로 $N-1$입니다.]
),
(
[- $D_2 = B_2 = 2$이고, $2 < i <= k+1$인 $i$에 대한 $D_i$와 $B_i$값은 아래와 같습니다.],
[#h(2em) #emoji.ast $D_i = sum_(j=2)^(i-1)S_j + 2$],
[#h(2em) #emoji.ast $B_i = i$#v(1em)],
[- $sum_(i=2)^(k+1)|D_i-B_i|=&|2-2|+|S_2+2-3|+|S_2+S_3+2-4|+dots#linebreak()
& + |sum^(k)_(j=2)S_j+2-(k+1)| #linebreak() &=|S_2-1| + |S_2+S_3-2| + dots + |sum^(k)_(j=2)S_j-(k-1)|$],
[- 여기에서 서브트리의 크기는 1 이상이므로, 절댓값 내부의 식을 빼내서 식을 정리할 수 있습니다.]
),
(
[- $sum_(i=2)^(k+1)|D_i-B_i| &= (S_2 - 1) + (S_2+S_3-2) + dots + (sum_(j=2)^(k)S_j-(k-1))#linebreak() &= (k-1)S_2 + (k-2)S_3 + ... + S_k - sum_(i=1)^(k-1)\i$],
[- 위 식이 최대가 되도록 $S_i$를 배분하는 것은 $S_2=N-k$, 나머지는 $1$로 두는 것입니다.],
[- $2$번 정점을 루트로 하는 서브트리에서 노드를 배치하는 형태는 $sum_(i=1)^N|D_i-B_i|$를 최대화하는 데 영향을 미치지 않습니다.],
[- 따라서 $k$값 중, 문제의 정답을 최대화하는 값을 찾아야 합니다.],
),
(
[#v(-2em)#align(center)[#image("images/bfsdfs.png", width: 50%)]],
[- 순회 순서의 차를 보면, $N-k-1$개에 해당하는 #math.op("BFS") 순서는 $k+1$씩 밀려납니다.],
[- 반대로, $k-1$개의 #math.op("DFS") 순서는 $N-k+1$씩 밀립니다.],
[- 따라서 $(k-1) times (N-k+1) + (k+1) times (N-k-1)$의 최댓값을 구해야 합니다.]
),
(
[- 식을 펼쳐 계산하면 $k=N/2$인 경우에 최대가 되며, 언급한 대로 트리를 구축해주면 됩니다.],
)
),
(
(
[- 팰린드롬 애너그램에서 보였듯, $N$이 짝수인 경우 항상 가능합니다.],
[- 홀수인 경우에는 한가운데의 원소가 자신의 자리에 있을 때에만 가능합니다.],
[- 어떻게 최소한의 연산으로 원하는 배열을 만들 수 있을까요?]
),
(
[- 다음과 같은 배열을 봅시다.],
[#align(center)[#table(columns:6)[#cell(2, color:PALE_RED)][#cell(1,color:PALE_RED)][#cell(5,color:blue)][#cell(3,color:blue)][#cell(6,color:blue)][#cell(4,color:blue)]]],
[- 이 배열이 정렬되기 위해서는 어떻게 해야 할 지 생각해 봅시다.],
[- 각 원소가 자신의 자리로 돌아가기 위해서, 현재 적혀있는 수에 해당하는 칸으로 간선을 그어 봅시다.],
[- 배열에서 사이클을 분리해낼 수 있습니다. 사이클의 종류는 총 세 가지입니다.],
[- 왼쪽 절반 또는 오른쪽 절반으로만 이루어져 있는 사이클, 교차하는 사이클],
[- 각각을 $L$, $R$, $M$ 사이클이라고 정의합시다.]
),
(
[- $L$ 사이클과 $R$ 사이클은 그 자체로 교환이 불가합니다.],
[- $M$ 사이클로 만들어서 정렬하는 과정이 필요합니다.],
[- $1$회 교환을 통해서 원소가 한 개 늘어난 $M$ 사이클을 만들 수 있습니다.],
[- 이때, $L$과 $R$ 사이클이 둘 다 존재한다면, 두 사이클의 원소를 서로 교환해서 하나의 $M$ 사이클을 만들 수 있습니다.],
[- 이는 각 사이클을 $M$ 사이클로 만들었을 때보다 교환을 $1$번 적게 할 수 있으므로, 가능하다면 $L$, $R$ 사이클 간 교환을 통해 $M$ 사이클을 만드는 것이 이득입니다.],
),
(
[- 이제 $M$ 사이클을 올바르게 배치하는 데 드는 비용을 알아봅시다.],
[- 크기가 $2$인 $M$ 사이클을 정렬하는 데에는 $1$번의 교환이면 충분합니다.],
[- 크기가 $i$인 $M$ 사이클을 정렬하는 데 걸리는 비용이 $k$라고 합시다.],
[- 크기가 $i+1$인 $M$ 사이클을 정렬할 때, 세 개 이상의 원소를 동시에 교환할 수 있는 방법이 없으므로 $1$번의 교환을 통해 크기가 $i$인 $M$ 사이클을 만드는 것이 최선입니다.],
[- $i+1$ 개의 원소를 가지는 $M$ 사이클은 $k+1$번 교환을 통해 정렬할 수 있습니다.],
[- $i=2$일 때 $k=1$이므로, 크기가 $N$인 $M$ 사이클은 $N-1$번의 교환을 통해 정렬할 수 있습니다.]
),
(
[- 따라서 $L$, $R$, $M$ 사이클의 개수를 각각 구해준 뒤, $L$, $R$ 사이클 쌍들을 $M$ 사이클들로 만들어 줍시다.],
[- 남는 $L$, $R$ 사이클은 크기가 $1$ 증가한 $M$ 사이클이 됩니다.],
[- 기존 $M$ 사이클은 각 사이클의 원소의 개수보다 $1$ 작은 횟수로 정렬이 가능합니다. ],
[- $M$ 사이클을 구성하는 왼쪽/오른쪽 원소의 개수가 같은 쪽으로 가도록 교환하면 $M$ 사이클을 유지하면서 크기를 $1$씩 줄여나갈 수 있습니다.],
[- 사이클의 개수, 종류, 구성하는 원소 등을 조합해 위에서 설명한 대로 답을 도출할 수 있습니다.],
[- 시간복잡도는 $cal(O)(N)$입니다.]
)
),
)
#let create_page(index) = {
set list(marker: text(fill:KUPC_GREEN)[✓])
for pg in descriptions.at(index) {
[
#constructTitle(contest_problems.at(index), size: 2em, bookmark:false)
#v(5em)
]
for desc in pg {
set text(size: 2em)
pad(left: -1.5em)[#desc]
v(0em);
}
pagebreak(weak: true)
}
}