Jekyll2022-05-21T17:15:37+00:00https://justicehui.github.io/rss/JusticeHui가 PS하는 블로그Let's solve problem with JusticeHui!JusticeHui백준18349 천지창조2022-05-01T23:59:00+00:002022-05-01T23:59:00+00:00https://justicehui.github.io/ps/2022/05/01/BOJ18349<h3 id="문제-링크">문제 링크</h3> <ul> <li>http://icpc.me/18349</li> </ul> <h3 id="풀이">풀이</h3> <p>문제를 천천히 읽어보면 문제를 풀기 위해 필요한 작업은 쉽게 알 수 있고, 이 작업들을 효율적으로 처리하는 것이 문제의 핵심입니다. 구체적으로, 다음과 같은 작업을 빠르게 수행해야 합니다. 각각의 개념을 모두 설명하는 것은 너무 어렵기 때문에, 문자열 파트를 제외한 다른 부분은 필요한 알고리즘과 설명 링크로 대신하겠습니다.</p> <ul> <li>Voronoi Diagram</li> <li>Minimum Spanning Tree</li> <li>Path Maximum Query on Dynamic Tree</li> <li>Nearest Point Search</li> <li>Longest Common Substring Query (Offline)</li> </ul> <p>보로노이 다이어그램은 Fortune’s Algorithm을 이용해 $O(N \log N)$에 구할 수 있습니다. (<a href="https://jacquesheunis.com/post/fortunes-algorithm/">설명</a>, <a href="https://github.com/zigui-ps/VoronoiDiagram/blob/master/teamnote_VoronoiDiagram.cpp">코드</a>)<br />보로노이 다이어그램의 듀얼 그래프는 들로네 삼각분할이므로 $O(N \log N)$에 들로네 삼각분할을 구해도 됩니다.</p> <p>이 문제에서 최소 스패닝 트리를 만들 때, 한 정점에서 시작해 트리 하나를 유지하는 방식으로 만들기 때문에 Prim’s Algorithm을 사용해야 합니다. 들로네 삼각분할(평면 그래프) 상의 간선만 사용하기 때문에 $O(N \log N)$에 구할 수 있습니다.</p> <p>다이나믹 트리에서 경로의 최댓값은 링크컷 트리를 이용해 amortized $O(\log N)$에 구할 수 있습니다. (<a href="https://justicehui.github.io/hard-algorithm/2021/01/01/link-cut-tree/">설명&amp;코드</a>)</p> <p>가장 가까운 점을 찾는 것은 보로노이 다이어그램에서 스위핑을 해도 되고, k-d tree를 사용해도 됩니다. (<a href="https://algoshitpo.github.io/2020/02/09/kdtree/">설명&amp;코드</a>)<br />평면을 쪼갤 때 반평면으로 쪼개면 주어진 점의 좌표가 작고 쿼리로 주어지는 점의 좌표가 클 때 시간 초과가 발생합니다. 반평면이 아닌 직사각형으로 쪼개야 시간 초과를 피할 수 있습니다. (thanks to @jh05013)</p> <h4 id="문자열-최장-공통-부분-문자열-쿼리">문자열 (최장 공통 부분 문자열 쿼리)</h4> <p>일반적으로 두 문자열 $S, T$의 최장 공통 부분 문자열을 찾는 것은 $S+T$의 LCP 배열을 구한 다음, $T$의 모든 접미사에 대해 LCP 배열 상에서 가장 가까운 $S$의 접미사까지의 구간 최댓값을 구하면 됩니다. 투포인터를 이용하면 $O(\vert S\vert + \vert T\vert)$ 시간에 구할 수 있습니다.<br />고정된 $S$에 대해 $T_i$와의 최장 공통 부분 문자열을 찾는 것도 $S+T_1+T_2+\cdots+T_k$에서 비슷한 방식으로 할 수 있습니다. LCP 배열을 구하는데 $O(\vert S\vert + \sum \vert T_i\vert)$, 쿼리는 각각 $O(\vert S\vert +\vert T_i\vert)$에 처리할 수 있으므로 전체 시간 복잡도는 $O(k\vert S\vert + \sum \vert T_i\vert)$입니다.</p> <p>만약 $\vert S_i\vert &lt; \vert S_j\vert$일 때 쿼리를 $O(\vert S_i\vert)$에 처리할 수 있다면, 문제에서 요구하는 $Q$개의 쿼리를 $O((Q+S)\sqrt S)$에 모두 처리할 수 있습니다. 이때 $S = \sum\vert S_i\vert$입니다.</p> <p>먼저, $S_1+S_2+\cdots+S_n$의 LCP 배열을 $O(S)$에 구합시다. 세 가지 경우로 나눠서 처리합니다.</p> <ol> <li>$\vert S_i\vert,\ \vert S_j\vert \leq \sqrt S$ <ul> <li>투포인터를 이용해 각 쿼리를 $O(\vert S_i\vert+\vert S_j\vert)$에 처리할 수 있습니다. 이 케이스는 최대 $Q$번 발생하고, $\vert S_i\vert,\ \vert S_j\vert \leq \sqrt S$이므로 전체 시간 복잡도는 $O(Q\sqrt S)$입니다.</li> </ul> </li> <li>$\vert S_i\vert \leq \sqrt S,\ \vert S_j\vert &gt; \sqrt S$ <ul> <li>길이가 $\sqrt S$보다 긴 문자열은 최대 $\sqrt S$개 존재합니다. 그러므로 접미사 배열에서 $S_j$의 접미사의 위치를 모았을 때, 임의의 지점에서의 lower bound를 $O(S\sqrt S)$에 모두 전처리할 수 있습니다.</li> <li>전처리된 lower bound를 이용하면 쿼리를 $O(\vert S_i\vert)$에 처리할 수 있고, $\vert S_i\vert \leq \sqrt S$이므로 전체 시간 복잡도는 $O(Q\sqrt S)$입니다.</li> </ul> </li> <li>$\vert S_i\vert,\ \vert S_j\vert &gt; \sqrt S$ <ul> <li>(2)에서 전처리된 배열을 사용하면 쿼리를 $O(\vert S_i\vert)$에 처리할 수 있습니다.</li> <li>길이가 $\sqrt S$보다 긴 문자열은 최대 $\sqrt S$개 존재하므로, 서로 다른 $(i, j)$ 순서쌍은 최대 $(\sqrt S)^2 = S$개 존재합니다. 전체 시간 복잡도는 $O(S\sqrt S)$입니다.</li> <li>쿼리 결과를 캐싱해야 합니다.</li> </ul> </li> </ol> <p>접미사 배열과 LCP 배열을 선형 시간에 구하고, 구간 최댓값 쿼리를 선형 시간 전처리 + 상수 시간에 처리하면 $O(Q+S\sqrt S)$에 모든 쿼리를 처리할 수 있습니다.</p> <p>Suffix Automaton을 사용해도 동일한 시간 복잡도로 쿼리를 처리할 수 있습니다. ($S$의 suffix automaton을 만드는데 $O(\vert S\vert)$가 걸리고, $T$와의 최장 공통 부분 문자열을 구하는데 $O(\vert T\vert)$가 걸립니다.)</p> <p>설명 링크 모음</p> <ul> <li>선형 시간 접미사 배열 (<a href="http://www.secmem.org/blog/2021/11/21/linear-suffix-array/">설명</a>, <a href="https://github.com/koosaga/olympiad/blob/master/Library/codes/string/suffix_array.cpp">코드</a>)</li> <li>선형 시간 전처리, 상수 시간 RMQ (<a href="https://codeforces.com/blog/entry/78931">설명&amp;코드</a>)</li> <li>Suffix Automaton (<a href="https://github.com/joy-mollick/Suffix-automata-Problems/blob/master/Longest%20Common%20Substring%20of%20Two%20Strings.cpp">코드</a>)</li> </ul>JusticeHui문제 링크 http://icpc.me/183492021년 국제정보올림피아드 선발고사 풀이2022-04-16T06:46:00+00:002022-04-16T06:46:00+00:00https://justicehui.github.io/koi/2022/04/16/2021tst<p>1차 2번(통신망), 2차 1번(총 쏘기), 2차 4번(가로등)은 아직 풀이가 준비되지 않았습니다.<br />풀이가 준비되지 않은 세 문제는 모두 Petrozavodsk Programming Camp Winter 2022에 사용되었으며, 영어로 된 만점 풀이는 <a href="https://codeforces.com/gym/103627/attachments/download/15719/gp20220202sol.pdf">캠프 에디토리얼</a>에서 확인할 수 있습니다. 통신망은 Critical Vertex, 총 쏘기는 Two Bullets, 가로등은 Streetlights입니다.</p> <h2 id="1-1-카드-뒤집기-게임-백준-22026">1-1. 카드 뒤집기 게임 (백준 22026)</h2> <h4 id="subtask-3-m-leq-n-leq-1000-100점">Subtask 3. $M \leq N \leq 1\,000$ (100점)</h4> <p>$0\cdots M-1$번째 행과 $0\cdots M-1$번째 열을 모두 카드에 그려진 패턴과 동일하게 만드는 방법은 유일합니다. 그러므로 $0\cdots M-1$번째 행과 열을 그리디하게 뒤집어서 카드에 그려진 패턴대로 만든 다음, 전체 격자의 카드와 패턴이 동일한지 확인하면 됩니다.<br />시간 복잡도는 $O(NM)$입니다.</p> <h2 id="1-2-통신망-백준-22027">1-2. 통신망 (백준 22027)</h2> <p>풀이 준비 중</p> <h2 id="1-3-렉-백준-22028">1-3. 렉 (백준 22028)</h2> <h4 id="subtask-2-m--0-16점">Subtask 2. $M = 0$ (16점)</h4> <p>각 직사각형 $[x_1,x_2]\times[y_1,y_2]$에 대해 $(x_1,y_1), (x_2+1,y_2+1)$에 1을 더하고 $(x_2+1,y_1), (x_1,y_2+1)$에 -1을 더한 뒤 2차원 누적합을 구하면, $(x,y)$의 정답이 $(x,y)$에 저장됩니다. $x$좌표 순서대로 스위핑하면서 펜윅 트리를 이용해 $y$축의 누적합을 관리하면 $O((N+Q) \log X)$에 문제를 해결할 수 있습니다.</p> <h4 id="subtask-4-상하좌우-이동-29점">Subtask 4. 상하좌우 이동 (29점)</h4> <p>직사각형이 위로 이동하는 것만 생각해 봅시다. 구체적으로, $[x_1,x_2]\times[y_1,y_2]$가 $y$좌표가 증가하는 방향으로 $d$만큼 이동하는 상황을 가정하겠습니다.</p> <p>Subtask 2처럼 2차원 누적합의 관점에서 생각하면, 위로 올라가는 것은 $(x_1,y_1+1\cdots y_1+d)$와 $(x_2+1,y_2+2\cdots y_2+d+1)$에 1을 더하고, $(x_2+1,y_1+1\cdots y_1+d)$와 $(x_1, y_2+2\cdots y_2+d+1)$에 -1을 더하는 것과 동일합니다. Subtask 2와 마찬가지로 $x$좌표 순서대로 스위핑을 하면서 $y$축의 누적합을 관리할 것입니다.<br />구간 $[s,e]$에 1을 더하고 누적합을 구하는 것은 $s, s+1, \cdots, e$에 $1, 2, \cdots, e-s+1$을 더하고, $e+1$ 이상인 지점에는 $e-s+1$을 더하는 것이라고 생각할 수 있습니다. 즉, 구간에 일차 함수를 더하는 것과 특정 값을 더하는 것으로 나눠서 생각할 수 있습니다. 펜윅 트리 2개를 이용해 할 수 있습니다.</p> <p>아래로 이동하는 것은 뒤집어서 처리하면 되고, 좌우로 이동하는 것은 $x, y$좌표를 바꿔서 처리하면 됩니다. $O((N+M+Q)\log X)$에 해결할 수 있습니다.</p> <h4 id="subtask-6-100점">Subtask 6. (100점)</h4> <p>$y=x$ 직선에 평행하게 이동하는 상황에서 $[1,Q_x]\times [1,Q_y]$ 영역의 합을 구하는 것은, 해당 영역에 포함되는 기울기가 1인 일차 함수들의 길이를 모두 더한 것이라고 생각할 수 있습니다. $x-y &gt; Q_x-Q_y \textit{ and } x \leq Q_x$인 삼각형 영역(오른쪽 아래)과 $ x-y \leq Q_x-Q_y \textit{ and } y \leq Q_y$인 삼각형 영역(왼쪽 위)으로 나눠서 생각합시다.<br />$(x,y)$를 $(y-x,x)$로 변환하면 원래 <strong>오른쪽 아래 영역</strong>에 있던 점이 모두 점 $Q$의 왼쪽 아래로 이동합니다. 마찬가지로 $(x,y)$를 $(x-y,y)$로 변환하면 <strong>왼쪽 위 영역</strong>에 있던 점이 모두 점 $Q$의 왼쪽 아래로 이동합니다. 각각 $y-x$와 $x-y$ 순서대로 스위핑하면 Subtask 4와 동일한 방법으로 처리할 수 있습니다.<br />좌표 변환 과정은 아래 텍스트에서 <code class="language-plaintext highlighter-rouge">I</code>에 주목해보세요.</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>GHI (x-y,y) GHI (y-x,x) CFI DEF &lt;--------- DEF --------&gt; BEH ABC ABC ADG </code></pre></div></div> <p>$y=-x$ 직선에 평행하게 이동하는 것도 비슷하게 처리할 수 있습니다. $x+y \leq Q_x+Q_y \textit{ and }y\leq Q_y$인 사다리꼴 영역에서 $x+y\leq Q_x+Q_y \textit{ and } x &gt; Q_x$인 삼각형 영역을 빼면 됩니다.<br />$(x,y)$를 $(x+y,y)$로 변환하면 <strong>사다리꼴 영역</strong>에 있던 점이 모두 점 $Q$의 왼쪽 아래로 이동하고, $(x+y,-x)$로 변환하면 <strong>삼각형 영역</strong>에 있던 점이 모두 $Q$의 왼쪽 아래로 이동합니다. 각각 $x+y$ 순서대로 스위핑하면 Subtask 4와 동일한 방법으로 처리할 수 있습니다.<br />좌표 변환 과정은 아래 텍스트에서 <code class="language-plaintext highlighter-rouge">13</code>에 주목해보세요.</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> 1 2 3 4 5 1 2 3 4 5 21 16 11 6 1 6 7 8 9 10 (x+y,y) 6 7 8 9 10 (x+y,-y) 22 17 12 7 2 11 12 13 14 15 &lt;-------- 11 12 13 14 15 ----------&gt; 23 18 13 8 3 16 17 18 19 20 16 17 18 19 20 24 19 14 9 4 21 22 23 24 25 21 22 23 24 25 25 20 15 10 5 </code></pre></div></div> <p>전체 시간 복잡도는 $O((N+M+Q)\log N)$입니다.</p> <h2 id="1-4-철도-백준-22029">1-4. 철도 (백준 22029)</h2> <h4 id="subtask-2-k--1-17점">Subtask 2. $K = 1$ (17점)</h4> <p>트리에 간선을 하나 추가하면 사이클이 정확히 한 개 존재하는 그래프가 됩니다. 이렇게 만들어진 사이클과 추가된 가짜 간선을 빠르게 찾을 수 있어야 합니다.</p> <p>사이클의 크기가 작을수록 고려해야 할 경우의 수가 적어지니까, 거리가 2인 두 정점을 가짜 간선으로 연결해서 크기가 3인 사이클을 만들어 봅시다. 가짜 간선으로 연결되지 않은 정점을 표시하면, 사이클에 속한 정점 중 표시되지 않은 두 정점을 연결한 간선이 가짜 간선입니다.<br />$N \leq 200$이므로 인접 행렬을 만든 다음 $O(N^3)$에 크기 3 사이클을 찾아도 충분합니다.</p> <h4 id="subtask-4-지름-leq-n2-46점">Subtask 4. 지름 $\leq N/2$ (46점)</h4> <p>정점 하나를 표시하는 것은 그 정점을 루트로 잡는 것이라고 생각할 수 있습니다. 트리에서 루트가 고정되어 있다면 DFS/BFS와 같은 그래프 순회를 통해 정점들의 순서를 붙일 수 있습니다.<br />특히 BFS를 이용하면 루트에서 각 정점 $v$까지 가는 거리 $D(v)$를 구할 수 있고, 일반적인 그래프에서 BFS 스패닝 트리에 속한 간선 $(u, v)$는 $\vert D(u) - D(v) \vert = 1$을 만족합니다. 즉, 아무 정점을 루트로 잡아서 BFS를 한 다음, 루트까지의 거리가 같은 정점들을 가짜 간선으로 연결하면 됩니다.</p> <p>이 풀이를 제출하면 100점을 받아야 할 것 같지만 46점이 나옵니다. 왜 그런지는 Subtask 5 풀이에서 살펴봅시다.</p> <h4 id="subtask-5-n-leq-500-100점">Subtask 5. $N \leq 500$ (100점)</h4> <p>트리가 선형이고 루트를 끝에 있는 정점으로 잡으면 거리가 같은 정점이 존재하지 않기 때문에 문제를 해결할 수 없습니다. 반대로 말하면, 거리가 같은 정점쌍이 $\lfloor N/2\rfloor-1$개 이상 존재하도록 만드는 루트를 찾으면 됩니다.</p> <p>여기까지 왔다면 다들 알겠지만 트리의 깊이를 $N/2$ 이하로 만들면 되고, 이는 지름의 중점이나 센트로이드를 루트로 잡으면 됩니다. 트리의 지름을 찾는 것과 센트로이드를 찾는 것 모두 선형 시간에 할 수 있기 때문에 $O(N)$에 문제를 해결할 수 있습니다.<br />사실 $N$의 크기가 작아서 모든 정점에 대해 시도해서 가짜 간선을 $K$개 만들 수 있는지 확인해도 됩니다.</p> <h2 id="2-1-총-쏘기-백준-22030">2-1. 총 쏘기 (백준 22030)</h2> <p>풀이 준비 중</p> <h2 id="2-2-회의실-백준-22031">2-2. 회의실 (백준 22031)</h2> <h4 id="subtask-2-k--1-17점-1">Subtask 2. $K = 1$ (17점)</h4> <p>Interval Graph가 주어졌을 때, 선분을 몇 개 제거해서 모든 컴포넌트의 크기가 $K$ 이하가 되도록 만드는 문제입니다. 이때 제거하는 선분의 가중치의 합을 최소화해야 합니다.</p> <p>$K = 1$이면 가중치가 있는 회의실 배정 문제이고, 끝점 기준으로 정렬한 뒤 DP를 하면 $O(N\log N)$에 해결할 수 있습니다. 회의실 배정 문제를 푼 다음, 전체 가중치 합에서 뺀 값을 출력하면 됩니다.</p> <h4 id="subtask-4-n-leq-250-53점">Subtask 4. $N \leq 250$ (53점)</h4> <p>끝점이 $i$ 이하인 선분들만 사용해서 만들 수 있는 가중치의 최댓값을 $D(i)$라고 정의합시다. 좌표 압축을 이용해 $i$의 범위를 $2N$으로 만들 수 있습니다.</p> <p>끝점이 $i$ 이하인 선분들만 사용하는 것은 $j &lt; i$인 $j$에 대해 $D(j)$를 구한 뒤, $(j, i]$ 구간에 완전히 포함되는 선분을 최대 $K$개 선책하는 것이라고 생각할 수 있습니다. 그러므로 $C(a,b)$를 $[a,b]$ 구간에 완전히 포함되는 최대 $K$개의 선분의 가중치 합이라고 정의하면 $D(i) = \max D_j + C(j+1,i)$입니다.<br />$C$ 배열은 $O(N^3)$에 모두 만들 수 있고 점화식은 $O(N^2)$에 계산할 수 있으므로 전체 시간 복잡도는 $O(N^3)$입니다.</p> <h4 id="subtask-5-n-leq-2500-150점">Subtask 5. $N \leq 2\,500$ (150점)</h4> <p>점화식을 계산하는 것 자체는 이미 $O(N^2)$으로 충분히 빠르기 때문에, $C$를 전처리하는 과정을 최적화해야 합니다. $i$를 고정하고 $j$를 증가시키면서 $C(i, j)$를 구할 때, 선분이 추가되는 상황에서 가장 큰 $K$개를 관리하는 것은 최소 힙을 이용해 수행할 수 있습니다. 즉, $C(i,\ast)$를 계산할 때 $O(N \log N)$이 걸리므로 $C$ 배열 전체를 모두 전처리하는 것은 $O(N^2 \log N)$ 시간에 할 수 있습니다.<br />점화식은 $O(N^2)$에 계산할 수 있으므로 전체 시간 복잡도는 $O(N^2 \log N)$입니다.</p> <h2 id="2-3-원숭이-백준-22032">2-3. 원숭이 (백준 22032)</h2> <h4 id="subtask-2-m-leq-5000-53점">Subtask 2. $M \leq 5\,000$ (53점)</h4> <p>원숭이가 잡을 수 있는 손잡이 쌍 $(x, y)$를 오름차순으로 정렬하면, 원숭이가 이동하는 경로는 정렬된 배열에서 인덱스가 증가하는 형태로 나타납니다. 그러므로 P 배열을 정렬합시다.</p> <p>$D(i)$를 $i$번째 순서쌍까지 도달했을 때 먹을 수 있는 바나나의 최대 개수라고 정의하면, $j &lt; i$이면서 $x_j = x_i$인 $j$에 대해 $D(i) \leftarrow D(j) + B_{y_i}$이고, $y_j = y_i$인 $j$에 대해 $D(i) \leftarrow D(j) + A_{x_i}$입니다. 점화식을 $O(N^2)$에 계산할 수 있습니다.</p> <h4 id="subtask-3-nm-leq-500000-150점">Subtask 3. $N,M \leq 500\,000$ (150점)</h4> <p>Subtask 2에서 세운 점화식을 $O(N^2)$보다 빠르게 계산해야 합니다.</p> <p>$i$번째 순서쌍까지 고려했을 때, 마지막에 잡은 기둥 A의 손잡이가 $x$이면서 먹을 수 있는 바나나의 최대 개수를 $L(i,x)$, 마지막에 잡은 기둥 B의 손잡이가 $y$이면서 먹을 수 있는 바나나의 최대 개수를 $R(i,y)$라고 정의합시다.<br />점화식은 $D(i) = \max\lbrace A_x + B_y, L(i-1,x)+B_y, A_x+R(i-1,y) \rbrace$, $L(i,x)\leftarrow L(i-1,x)+D(i)$, $R(i,y) \leftarrow R(i-1,y)+D(i)$입니다.<br />$L(i-1,\ast)$에서 $L(i,\ast)$로 전이할 때 값이 바뀌는 위치는 $x_i$ 밖에 없으므로 메모리를 $O(N)$만 사용할 수 있습니다. 마찬가지로 $R$ 배열도 메모리를 $O(N)$만 사용해서 표현할 수 있습니다.<br />P 배열을 정렬하는데 $O(M \log M)$이 걸리고, $D, L, R$ 배열을 계산하는 것은 $O(N)$이 걸리므로, 전체 시간 복잡도는 $O(N+M\log M)$입니다.</p> <h2 id="2-4-가로등-백준-22033">2-4. 가로등 (백준 22033)</h2> <p>풀이 준비 중</p>JusticeHui1차 2번(통신망), 2차 1번(총 쏘기), 2차 4번(가로등)은 아직 풀이가 준비되지 않았습니다.풀이가 준비되지 않은 세 문제는 모두 Petrozavodsk Programming Camp Winter 2022에 사용되었으며, 영어로 된 만점 풀이는 캠프 에디토리얼에서 확인할 수 있습니다. 통신망은 Critical Vertex, 총 쏘기는 Two Bullets, 가로등은 Streetlights입니다.2020년 국제정보올림피아드 선발고사 풀이2022-04-14T14:45:00+00:002022-04-14T14:45:00+00:00https://justicehui.github.io/koi/2022/04/14/2020tst<p>2020년 1월 17일과 7월 12일에 열린 선발고사 문제 풀이입니다.<br /> 1차 3번(지름길), 2차 2번(하나 둘 셋), 2차 3번(열차의 이동)은 아직 풀이가 준비되지 않았습니다.</p> <h2 id="1-1-문자열-찾기-백준-25008">1-1. 문자열 찾기 (백준 25008)</h2> <h4 id="subtask-1-n--m-8점">Subtask 1. $N = M$ (8점)</h4> <p>$f(S_i) = P_i$인 일대응 대응 함수 $f$가 존재하는지 확인하면 되고, $O(N)$에 해결할 수 있습니다.</p> <h4 id="subtask-3-n-leq-2000-25점">Subtask 3. $N \leq 2\,000$ (25점)</h4> <p>$S$의 길이가 $M$인 모든 부분 문자열에 대해 Subtask 1의 풀이를 적용하면 $O(NM)$에 해결할 수 있습니다.</p> <h4 id="subtask-4-n-leq-1000000-100점">Subtask 4. $N \leq 1\,000\,000$ (100점)</h4> <p>KMP 알고리즘을 생각해 봅시다. KMP 알고리즘은 패턴의 실패 함수를 구한 뒤, 원본 문자열과 패턴 문자열을 한 글자씩 비교하면서 매칭에 실패하는 경우 실패 함수를 따라가는 방식으로 진행합니다. 패턴 $P$가 문자열 $S$의 부분 문자열인지 판별하는 가장 기초적인 KMP 알고리즘의 구현은 다음과 같습니다.</p> <div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">vector</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span> <span class="n">GetFail</span><span class="p">(</span><span class="k">const</span> <span class="n">string</span> <span class="o">&amp;</span><span class="n">P</span><span class="p">){</span> <span class="kt">int</span> <span class="n">n</span> <span class="o">=</span> <span class="n">P</span><span class="p">.</span><span class="n">size</span><span class="p">();</span> <span class="n">vector</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span> <span class="n">Fail</span><span class="p">(</span><span class="n">n</span><span class="p">);</span> <span class="k">for</span><span class="p">(</span><span class="kt">int</span> <span class="n">i</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span> <span class="n">j</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span> <span class="n">i</span><span class="o">&lt;</span><span class="n">n</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">){</span> <span class="k">while</span><span class="p">(</span><span class="n">j</span> <span class="o">&gt;</span> <span class="mi">0</span> <span class="o">&amp;&amp;</span> <span class="n">P</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">==</span> <span class="n">P</span><span class="p">[</span><span class="n">j</span><span class="p">])</span> <span class="n">j</span> <span class="o">=</span> <span class="n">Fail</span><span class="p">[</span><span class="n">j</span><span class="o">-</span><span class="mi">1</span><span class="p">];</span> <span class="k">if</span><span class="p">(</span><span class="n">P</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">==</span> <span class="n">P</span><span class="p">[</span><span class="n">j</span><span class="p">])</span> <span class="n">Fail</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="o">++</span><span class="n">j</span><span class="p">;</span> <span class="p">}</span> <span class="k">return</span> <span class="n">Fail</span><span class="p">;</span> <span class="p">}</span> <span class="n">vector</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span> <span class="n">KMP</span><span class="p">(</span><span class="k">const</span> <span class="n">string</span> <span class="o">&amp;</span><span class="n">S</span><span class="p">,</span> <span class="k">const</span> <span class="n">string</span> <span class="o">&amp;</span><span class="n">P</span><span class="p">){</span> <span class="kt">int</span> <span class="n">n</span> <span class="o">=</span> <span class="n">S</span><span class="p">.</span><span class="n">size</span><span class="p">(),</span> <span class="n">m</span> <span class="o">=</span> <span class="n">P</span><span class="p">.</span><span class="n">size</span><span class="p">();</span> <span class="n">vector</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span> <span class="n">Fail</span> <span class="o">=</span> <span class="n">GetFail</span><span class="p">(</span><span class="n">P</span><span class="p">),</span> <span class="n">R</span><span class="p">;</span> <span class="k">for</span><span class="p">(</span><span class="kt">int</span> <span class="n">i</span><span class="o">=</span><span class="mi">0</span><span class="p">,</span> <span class="n">j</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span> <span class="n">i</span><span class="o">&lt;</span><span class="n">n</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">){</span> <span class="k">while</span><span class="p">(</span><span class="n">j</span> <span class="o">&gt;</span> <span class="mi">0</span> <span class="o">&amp;&amp;</span> <span class="n">S</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">!=</span> <span class="n">P</span><span class="p">[</span><span class="n">j</span><span class="p">])</span> <span class="n">j</span> <span class="o">=</span> <span class="n">Fail</span><span class="p">[</span><span class="n">j</span><span class="o">-</span><span class="mi">1</span><span class="p">];</span> <span class="k">if</span><span class="p">(</span><span class="n">S</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">==</span> <span class="n">P</span><span class="p">[</span><span class="n">j</span><span class="p">]){</span> <span class="k">if</span><span class="p">(</span><span class="n">j</span> <span class="o">+</span> <span class="mi">1</span> <span class="o">==</span> <span class="n">m</span><span class="p">)</span> <span class="n">R</span><span class="p">.</span><span class="n">push_back</span><span class="p">(</span><span class="n">i</span><span class="o">-</span><span class="n">m</span><span class="o">+</span><span class="mi">1</span><span class="p">),</span> <span class="n">j</span> <span class="o">=</span> <span class="n">Fail</span><span class="p">[</span><span class="n">j</span><span class="p">];</span> <span class="k">else</span> <span class="n">j</span><span class="o">++</span><span class="p">;</span> <span class="p">}</span> <span class="p">}</span> <span class="k">return</span> <span class="n">R</span><span class="p">;</span> <span class="p">}</span> </code></pre></div></div> <p>5번째 줄의 <code class="language-plaintext highlighter-rouge">P[i] == P[j]</code>는 단순히 <code class="language-plaintext highlighter-rouge">P[i]</code>와 <code class="language-plaintext highlighter-rouge">P[j]</code>가 동일한지 비교하는 것이 아닌, <code class="language-plaintext highlighter-rouge">P[0..j-1]</code>과 <code class="language-plaintext highlighter-rouge">P[i-j..i-1]</code>이 동치인 상황에서 한 글자씩 추가한<code class="language-plaintext highlighter-rouge">P[0..j]</code>와 <code class="language-plaintext highlighter-rouge">P[i-j..i]</code>가 동치인지 확인하는 부분입니다. 마찬가지로 14번째 줄의 <code class="language-plaintext highlighter-rouge">S[i] == P[j]</code>도 <code class="language-plaintext highlighter-rouge">P[0..j]</code>와 <code class="language-plaintext highlighter-rouge">S[i-j..i]</code>가 동치인지 확인하는 조건식입니다.<br />즉, <strong>사실상 같은 문자열</strong> $A, B$가 있을 때, 뒤에 각각 $a, b$를 붙인 $A+a$와 $B+b$가 <strong>사실상 같은 문자열</strong>인지 빠르게 확인할 수 있다면 KMP 알고리즘을 이용해 문제를 해결할 수 있습니다.</p> <p>만약 문자열 $A$에 이미 문자 $a$가 등장했다면 $a$는 매칭되어야 하는 짝이 정해져 있습니다. 반대로, 등장한 적이 없다면 아직 매칭되지 않은 임의의 문자와 매칭해도 됩니다.<br />이 정보는 각 문자마다 가장 최근에 등장한 위치를 저장하고 있다면 빠르게 처리할 수 있습니다. 구체적으로, <code class="language-plaintext highlighter-rouge">P[i]</code>가 <code class="language-plaintext highlighter-rouge">i</code> 이전에서 나온 가장 최근의 위치 <code class="language-plaintext highlighter-rouge">Prv[i]</code>를 알고 있다면 상수 시간에 <strong>사실상 같은 문자열</strong>인지 확인할 수 있습니다.</p> <p>전체 시간 복잡도는 $O(N+M)$입니다.</p> <h2 id="1-2-뚫기-백준-25009">1-2. 뚫기 (백준 25009)</h2> <h4 id="subtask-1-n-leq-3000-m-leq-3000-q1-7점">Subtask 1. $N \leq 3\,000, M \leq 3\,000, Q=1$ (7점)</h4> <p>사실 $M \leq 3\,000$ 조건이 없더라도 좌표 압축을 하면 $M = O(N)$으로 생각할 수 있습니다.</p> <p>$i-1$번째 줄에서 $(i, j)$로 이동하는 것은 $(i-1, k)$에서 앞으로 한 칸 전진해 $(i, k)$로 이동한 뒤, 비용이 $A$인 연산을 이용해 $(i, j)$로 이동하는, 두 개의 단계로 나눌 수 있습니다. DP 배열을 다음과 같이 정의하겠습니다.</p> <ul> <li>$D(2i, j) = $ 마지막에 앞으로 한 칸 전진해서 $(i, j)$에 도달하는 최소 비용</li> <li>$D(2i+1, j) = $ $(i, j)$에 도달하는 최소 비용</li> </ul> <p>$D(2i, j) = D(2i-1, j) + (0 \textit{ or } B)$이고, $D(2i+1,j) = \min\lbrace D(2i,j), D(2i,k)+A \rbrace$ 입니다. $O(N^2)$에 계산할 수 있습니다.</p> <h4 id="subtask-2-q-leq-50-29점">Subtask 2. $Q \leq 50$ (29점)</h4> <p>$D(i,\ast)$를 하나의 세그먼트 트리로 관리합니다. $D(i-1, \ast)$를 빠르게 $D(i, \ast)$로 바꾸는 방법을 찾아야 합니다.</p> <p>$D(2i,j) = D(2i-1, j) + B$는 막이 존재하는 구간에 $B$를 더하는 것과 동일하고, 이는 세그먼트 트리와 레이지 프로퍼게이션을 이용해 $O(\log N)$에 수행할 수 있습니다.<br />$D(2i+1,j) = \min\lbrace D(2i,j), D(2i,k)+A \rbrace$는 $D(2i,\ast)$의 최솟값 $D(2i,k)$를 구한 다음, 트리의 기존 값과 $D(2i,k)+A$ 중 더 작은 값으로 갱신해야 합니다. 이는 세그먼트 트리 비츠를 이용해 $O(\log N)$에 수행할 수 있습니다.</p> <p>각 쿼리를 $O(N \log N)$에 처리할 수 있으므로 전체 시간 복잡도는 $O(QN \log N)$입니다.</p> <h4 id="subtask-5-n-leq-10000-100점">Subtask 5. $N \leq 10\,000$ (100점)</h4> <p>Subtask 1/2의 풀이를 아무리 최적화해도 쿼리를 $O(N \log N)$보다 빠르게 처리할 수는 없어 보입니다. 대신, 각 연산을 각각 최대 $N$번만 사용해도 최적해를 찾을 수 있다는 점을 이용해 다른 풀이를 생각해 봅시다.</p> <p>만약 모든 $0 \leq i \leq N$에 대해, 비용이 $A$인 연산을 정확히 $i$번 사용했을 때 필요한 $B$ 연산의 횟수를 알고 있다면, $i$가 증가할수록 필요한 $B$ 연산의 횟수는 단조감소하므로 이분 탐색을 이용해 각 쿼리를 $O(\log N)$에 처리할 수 있습니다. 모든 $i$에 대해 직접 계산해보면 계산 방법에 따라 Subtask 3/4를 해결할 수 있습니다.</p> <p>도착점까지 갈 때 필요한 A 연산의 횟수 $a$와 B 연산의 횟수 $b$를 2차원 점 $(a, b)$로 나타내보면, 볼록 껍질의 <strong>왼쪽 아래 부분</strong>을 구성하는 점만 실제 정답이 될 수 있다는 것을 알 수 있습니다. 좌표 범위가 $X$일 때 볼록 껍질의 꼭짓점이 될 수 있는 정수 격자점은 최대 $O(X^{2/3})$개이기 때문에, $N+1$개의 점에서 모두 DP를 하는 것 대신 왼쪽 아래 껍질 상의 점에 대해서만 DP를 하면 시간 복잡도를 줄일 수 있습니다.</p> <p>필요한 점을 빠르게 찾는 방법을 알아봅시다. 먼저, x좌표가 가장 작은 왼쪽에 있는 점 $L$과 y좌표가 가장 작은 아래에 있는 점 $U$는 항상 껍질에 포함됩니다. 그러므로 두 점을 먼저 구합니다.<br />그 다음에는 $L$과 $U$를 잇는 직선과 평행한 껍질의 접선의 접점 $M$을 찾습니다. 다시 $L$과 $M$을 잇는 직선, $M$과 $U$를 잇는 직선의 기울기를 재귀적으로 처리하는 과정을 통해 모든 점을 찾아줄 수 있습니다.</p> <p>만약 <strong>주어진 기울기에 대한 볼록 껍질의 접점</strong>을 $T(N)$ 시간에 찾을 수 있다면 모든 점을 $O(N^{2/3}T(N))$에 구할 수 있습니다. 이때 $L$과 $U$는 기울기가 $1/0, 0/1$인 접선의 접점입니다.<br />기울기가 $-p/q\ (p,q \geq 0)$인 접점을 구하는 것은 A 연산의 비용이 $p$, B 연산의 비용이 $q$인 문제를 해결하는 것으로 생각할 수 있습니다. 이는 Subtask 2의 풀이를 이용해 $O(N \log N)$에 해결할 수 있습니다.</p> <p>$T(N) = O(N \log N)$이므로 $O(N^{2/3} \cdot N \log N)$에 모든 점을 계산할 수 있습니다. 쿼리는 $O(\log N)$에 처리할 수 있으므로 전체 시간 복잡도는 $O((N^{5/3} + Q) \log N)$입니다. 만약 세그먼트 트리 대신 평방 분할을 사용하면 $O(N^{13/6} + Q \log N)$이 됩니다.</p> <h2 id="1-3-지름길-백준-25010">1-3. 지름길 (백준 25010)</h2> <p>풀이 준비 중</p> <h2 id="1-4-칠하기-백준-25011">1-4. 칠하기 (백준 25011)</h2> <h4 id="subtask-2-nm-leq-1000-100점">Subtask 2. $N,M \leq 1\,000$ (100점)</h4> <p>가로 방향으로 연속한 칸 그룹과 세로 방향으로 연속한 칸 그룹을 정점으로 생각하면 방향 그래프를 만들 수 있습니다. 만들어진 모든 정점을 지나는 보행(walk)이 존재하는지 판별하는 문제로 생각할 수 있습니다.</p> <p>어떤 정점 $v$를 방문할 수 있다면 $v$와 같은 SCC에 속한 정점을 모두 방문할 수 있기 때문에 SCC를 압축한 DAG에서 문제를 해결해도 됩니다. DAG의 한 정점에서 출발해 모든 정점을 방문하는 walk가 있는지 판별하면 되고, Kosaraju’s Algorithm는 위상정렬 순서대로 SCC를 찾기 때문에 $i$번째 SCC에서 $i+1$번째 SCC로 가는 간선이 있는지 확인하면 됩니다.</p> <p>시간 복잡도는 $O(NM)$입니다.</p> <h2 id="2-1-마법의-다이얼-백준-25012">2-1. 마법의 다이얼 (백준 25012)</h2> <h4 id="subtask-1-3-n-leq-5000-26점">Subtask 1, 3. $N \leq 5\,000$ (26점)</h4> <p>$M$개의 다이얼 중 하나는 돌리지 않아도 최적해를 찾을 수 있습니다.</p> <p>$f(i,j)$를 $i$번째 다이얼을 돌리지 않으면서 $j$번째 줄에 $M$개의 점이 오도록 만드는 최소 비용이라고 정의합니다. $N$개의 모든 점에 대해 $f$를 계산한 뒤 최솟값을 취하면 되고, $f$는 매번 $O(N)$에 계산할 수 있습니다. 전체 시간 복잡도는 $O(N^2)$입니다.</p> <h4 id="subtask-2-m-leq-5000-r-leq-5000-41점">Subtask 2. $M \leq 5\,000,\ R \leq 5\,000$ (41점)</h4> <p>$f(j)$를 $j$번째 줄에 $M$개의 점이 오도록 만드는 최소 비용이라고 정의합시다. $f$는 매번 $O(N)$에 계산할 수 있습니다.<br />다이얼 하나를 고정하더라도 최적해를 찾을 수 있기 때문에, 실제로 확인해야 하는 $j$는 $\min(N,R)$가지입니다. $f(j)$는 각 다이얼마다 $j$와 가장 가까운 점을 찾아서 거리를 더하는 것으로 구할 수 있고, 이분 탐색을 이용해 $f(j)$를 $O(M \log N)$에 계산할 수 있습니다. 전체 시간 복잡도는 $O(RM \log N)$입니다.</p> <h4 id="subtask-4-n-leq-500000-r-leq-109-150점">Subtask 4. $N \leq 500\,000, R \leq 10^9$ (150점)</h4> <p>$f_i(j)$를 $i$번째 다이얼의 $j$번째 줄에 점이 오도록 만드는 최소 비용이라고 정의합시다. $f(j) = \sum f_i(j)$입니다.<br />함수 $f_i(j)$의 개형을 생각해보면, 기울기가 1 또는 -1인 <code class="language-plaintext highlighter-rouge">\/\/\/\...</code> 형태이고, 기울기가 바뀌는 지점은 점의 개수에 비례한다는 것을 알 수 있습니다. 그러므로 $f_i(j)$의 기울기가 바뀌는 지점과 변화량을 모두 구한다면 $f(j)$는 단순히 해당 정보들을 합쳐주는 것으로 구할 수 있습니다.</p> <p>$i$번째 다이얼의 $a_1 &lt; a_2 &lt; \cdots &lt; a_k$번째 줄에 점이 있다고 가정합시다. $a_x \leq j \leq a_{x+1}$인 $j$에 대해, $j \leq (a_x+a_{x+1})/2$이면 $f_i(j) = j-a_x$이고, $j \geq (a_x+a_{x+1})/2$이면 $f_i(j) = a_{x+1}-j$입니다. $j &lt; a_1 \lor j &gt; a_k$인 경우만 예외처리하면 됩니다.<br />함수 $f_i(j)$의 개형은 기울기가 1 또는 -1인 <code class="language-plaintext highlighter-rouge">\/\/\...</code> 형태라는 것을 알 수 있고, 기울기가 바뀌는 지점은 점의 개수에 비례한다는 것을 알 수 있습니다. 그러므로 $f_i(j)$의 기울기가 바뀌는 지점과 변화량을 모두 구하고, 이 정보들을 모두 합쳐서 $f(j)$ 함수를 만들 수 있습니다.</p> <p>기울기가 바뀌는 지점은 반정수이므로 2를 곱하면 쉽게 처리할 수 있습니다.</p> <p><code class="language-plaintext highlighter-rouge">std::map</code> 등을 이용해 $O(N \log N)$에 해결할 수 있습니다.</p> <h2 id="2-2-하나-둘-셋-백준-25013">2-2. 하나 둘 셋 (백준 25013)</h2> <p>풀이 준비 중</p> <h2 id="2-3-열차의-이동-백준-25014">2-3. 열차의 이동 (백준 25014)</h2> <p>풀이 준비 중</p> <h2 id="2-4-아이싱-백준-25015">2-4. 아이싱 (백준 25015)</h2> <h4 id="subtask-6-nm-leq-250000-150점">Subtask 6. $N,M \leq 250\,000$ (150점)</h4> <p><a href="https://justicehui.github.io/ps/2020/09/16/BOJ19862/">풀이</a></p>JusticeHui2020년 1월 17일과 7월 12일에 열린 선발고사 문제 풀이입니다. 1차 3번(지름길), 2차 2번(하나 둘 셋), 2차 3번(열차의 이동)은 아직 풀이가 준비되지 않았습니다.Rotating Sweep Line Technique (Bulldozer Trick)2022-03-30T14:59:00+00:002022-03-30T14:59:00+00:00https://justicehui.github.io/hard-algorithm/2022/03/30/rotate-sweep-line<h3 id="서론">서론</h3> <p>문제를 많이 풀어본 분들이라면 데이터가 정렬되어 있다는 것이 얼마나 좋은 성질인지 잘 알고 계실 것입니다. 이진 탐색을 이용할 수도 있고, 모든 원소가 서로 다른 배열에서 $A_i$보다 작은 가장 큰 원소와 같은 질의를 단순히 $A_{i-1}$을 반환하는 것으로 해결할 수 있습니다.<br /> 주어진 데이터가 정수와 같이 순서가 자명하게 정의되어 있다면 단순히 정렬하면 되지만, 2차원상의 점이라면 어떤 축을 기준으로 보느냐에 따라 정렬의 결과가 달라질 수 있습니다. 흔히 “불도저 트릭”이라고 불리는 이 테크닉은 서로 다른 정렬의 결과가 $O(N^2)$가지임을 보이고, 가능한 모든 정렬 결과를 $O(N^2 \log N)$에 순회하는 테크닉입니다.</p> <p>아직 널리 쓰이는 이름이 없습니다. 제 주변에서 많이 사용하는 명칭 두 개를 임의로 선택해 제목으로 정한 점 양해 부탁드립니다.</p> <h3 id="정렬의-경우의-수">정렬의 경우의 수</h3> <p>보통 점 $(x_i, y_i)$를 정렬하라고 하면 x좌표 오름차순으로 정렬합니다. 이 정렬 방법은 y축에 평행한 직선(기울기가 $\infty$인 직선)이 왼쪽부터 오른쪽으로 차례대로 이동하면서 만나는 점들에 순서대로 번호를 붙이는 것과 동일합니다. 마찬가지로, y좌표 내림차순으로 정렬하는 것은 x축에 평행한 직선(기울기가 $0$인 직선)이 한 방향으로 이동하면서 만나는 점들에 순서대로 번호를 붙이는 것입니다.<br /> 그러므로 서로 다른 정렬의 결과가 $O(N^2)$가지임을 보이는 것은, 정렬 기준으로 유효한 기울기가 $O(N^2)$개밖에 없다는 것을 보이면 됩니다.</p> <p><img src="https://i.imgur.com/rCZ8FgL.png" alt="" /></p> <p>정렬 기준 기울기를 아주 미세하게 변화시키더라도 정렬 결과는 바뀌지 않는다는 것은 직관적으로 알 수 있습니다. 아래 그림에서 초록색 화살표는 빨간색 화살표를 시계방향으로 4도 회전시킨 것이고, 정렬 결과가 바뀌지 않은 것을 확인할 수 있습니다.</p> <p><img src="https://i.imgur.com/O7BjTSc.png" alt="" /></p> <p>시계방향으로 계속 회전시키다 보면 4번 점과 5번 점을 잇는 선분과 평행해지는 시점에 4번 점과 5번 점의 우선순위가 동등해집니다. 이 상황에서 반시계방향으로 아주 조금 돌리면 x좌표가 더 작은 점이 앞에 오고, 시계방향으로 아주 조금 돌리면 x좌표가 더 큰 점이 앞에 오게 됩니다. 즉, 4번 점과 5번 점을 잇는 선분의 기울기를 기점으로 정렬 결과가 달라집니다.<br /> 그다음은 2번 점과 3번 점을 잇는 선분과 평행해지는 시점에 두 점의 순서가 바뀝니다.</p> <p><img src="https://i.imgur.com/FpuDkK7.png" alt="" /></p> <p>위 그림에서 알 수 있듯, 정렬 기준과 두 점을 잇는 선분과 평행해지는 시점에만 정렬 결과가 달라집니다. 또한 세 점이 한 직선 위에 존재하지 않는다면 항상 두 점의 순서만 바뀌고, 순서가 바뀌는 두 점은 정렬 순서상에서 인접합니다. 한 점을 기준으로 여러 기울기를 그렸을 때 자신보다 앞에 있는 점 집합과 뒤에 있는 점 집합이 어떻게 변화하는지 살펴보면 쉽게 알 수 있습니다.<br /> 두 점을 잇는 선분의 기울기만 봐도 모든 정렬의 결과를 확인할 수 있으므로, 가능한 정렬 결과의 경우의 수는 $O(N^2)$가지입니다. $O(N^2)$개의 기울기를 모두 구해서 정렬한 다음, 차례대로 순회하면서 점들의 순서를 바꾸는 방식으로 구현합니다.</p> <video autoplay="" controls="" loop="" preload="auto" style="max-width:100%"> <source src="/img/rotating-sweep-line.mp4" type="video/mp4" /> </video> <h3 id="활용">활용</h3> <p>구현 방법은 뒤에서 살펴보고, 이 테크닉을 어디에 활용할 수 있을지 먼저 알아보겠습니다.</p> <h4 id="boj-9484-최대삼각형-최소삼각형">BOJ 9484. 최대삼각형, 최소삼각형</h4> <p>$N$개의 점이 주어졌을 때, 3개의 점을 선택해서 만들 수 있는 삼각형 중 넓이의 최댓값과 최솟값을 구하는 문제입니다.</p> <p>삼각형의 넓이는 밑변의 길이와 높이의 곱을 2로 나눠서 구할 수 있습니다. 만약 밑변으로 사용할 두 꼭짓점을 고정한다면, 두 꼭짓점을 잇는 직선과 가장 가까운 점을 선택하면 최소 넓이 삼각형을 얻을 수 있습니다. 마찬가지로 직선과 가장과 가장 먼 점을 선택하면 최대 넓이를 얻을 수 있습니다.</p> <p>이 문제는 Rotating Sweep Line을 이용해 해결할 수 있습니다. 두 점을 잇는 선분의 기울기를 기준으로 정렬했고, 두 점의 번호를 각각 $u, v\ (u &lt; v)$라고 하겠습니다. 두 점을 잇는 직선과 가장 가까운 점은 $u-1, v+1$ 중 하나이며, 가장 먼 점은 $1, N$ 중 하나입니다. 그러므로 단순히 점들의 정렬 순서만 관리하면 이 문제를 해결할 수 있습니다.</p> <h4 id="boj-3121-빨간점-파란점">BOJ 3121. 빨간점, 파란점</h4> <p>좌표 평면 위에 빨간 점과 파란 점이 주어집니다. 평행선 사이에 파란 점이 존재하지 않도록 두 평행선을 그었을 때, 평행선 사이에 들어갈 수 있는 빨간 점 개수의 최댓값을 구하는 문제입니다.</p> <p>평행선의 기울기를 기준으로 정렬했을 때 인접한 점이 두 평행선 사이에 함께 들어갑니다. 그러므로 기울기를 고정했다면, 수열에서 0이 포함되지 않고 1로만 구성된 가장 긴 연속 부분 수열을 구하는 문제라고 생각할 수 있습니다. 이러한 문제는 흔히 금광 세그라고 불리는 세그먼트 트리를 이용해 쉽게 해결할 수 있습니다.</p> <p>$O(N^2)$개의 기울기에 대해 모두 수열에서의 문제를 푸는 것도 어렵지 않게 할 수 있습니다. 기울기마다 순서가 바뀌는 점은 2개이므로, 단순히 세그먼트 트리에서 리프 정점의 정보를 갱신하는 것으로 정렬 결과의 변화를 반영할 수 있습니다. 따라서 $O(N^2 \log N)$에 문제를 해결할 수 있습니다.</p> <h3 id="구현">구현</h3> <p>세 점 이상이 한 직선 위에 있는 경우, 해당 기울기에서 그들의 순서를 바꾸는 것은 구간을 뒤집는 것으로 생각할 수 있습니다.</p> <p><img src="https://i.imgur.com/SSGziv5.png" alt="" /></p> <p>정렬 순서상에서 인접한 점끼리만 위치를 교환해야 하기 때문에 평행한 선분을 아무 순서대로 처리하면 안 됩니다. $i, i+1, \cdots, j$번 점을 뒤집을 때, 먼저 $i$를 $i+1,\cdots,j$와 교환해서 맨 뒤로 보내고, $i+1$를 $i+2,\cdots,j$와 교환해서 뒤로 보내는 식으로 구간을 뒤집으면 됩니다.</p> <p>아래 코드는 BOJ 9484. 최대삼각형, 최소삼각형의 코드입니다. 점들의 초기 인덱스는 x좌표 오름차순으로 정렬했을 때의 순서로 정의했습니다.<br /> 기울기가 같은 선분을 잘 처리하기 위해서 23번째 줄에서 기울기가 같은 경우, 선분이 연결하는 두 점의 초기 인덱스를 비교해서 정렬합니다.</p> <div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include &lt;bits/stdc++.h&gt; </span><span class="k">using</span> <span class="k">namespace</span> <span class="n">std</span><span class="p">;</span> <span class="k">using</span> <span class="n">ll</span> <span class="o">=</span> <span class="kt">long</span> <span class="kt">long</span><span class="p">;</span> <span class="k">struct</span> <span class="nc">Point</span><span class="p">{</span> <span class="n">ll</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">;</span> <span class="kt">bool</span> <span class="k">operator</span> <span class="o">&lt;</span> <span class="p">(</span><span class="k">const</span> <span class="n">Point</span> <span class="o">&amp;</span><span class="n">p</span><span class="p">)</span> <span class="k">const</span> <span class="p">{</span> <span class="k">return</span> <span class="n">tie</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">)</span> <span class="o">&lt;</span> <span class="n">tie</span><span class="p">(</span><span class="n">p</span><span class="p">.</span><span class="n">x</span><span class="p">,</span> <span class="n">p</span><span class="p">.</span><span class="n">y</span><span class="p">);</span> <span class="p">}</span> <span class="kt">bool</span> <span class="k">operator</span> <span class="o">==</span> <span class="p">(</span><span class="k">const</span> <span class="n">Point</span> <span class="o">&amp;</span><span class="n">p</span><span class="p">)</span> <span class="k">const</span> <span class="p">{</span> <span class="k">return</span> <span class="n">tie</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">)</span> <span class="o">==</span> <span class="n">tie</span><span class="p">(</span><span class="n">p</span><span class="p">.</span><span class="n">x</span><span class="p">,</span> <span class="n">p</span><span class="p">.</span><span class="n">y</span><span class="p">);</span> <span class="p">}</span> <span class="p">};</span> <span class="k">struct</span> <span class="nc">Line</span><span class="p">{</span> <span class="n">ll</span> <span class="n">i</span><span class="p">,</span> <span class="n">j</span><span class="p">,</span> <span class="n">dx</span><span class="p">,</span> <span class="n">dy</span><span class="p">;</span> <span class="c1">// i &lt; j, dx &gt;= 0</span> <span class="n">Line</span><span class="p">(</span><span class="kt">int</span> <span class="n">i</span><span class="p">,</span> <span class="kt">int</span> <span class="n">j</span><span class="p">,</span> <span class="k">const</span> <span class="n">Point</span> <span class="o">&amp;</span><span class="n">pi</span><span class="p">,</span> <span class="k">const</span> <span class="n">Point</span> <span class="o">&amp;</span><span class="n">pj</span><span class="p">)</span> <span class="o">:</span> <span class="n">i</span><span class="p">(</span><span class="n">i</span><span class="p">),</span> <span class="n">j</span><span class="p">(</span><span class="n">j</span><span class="p">),</span> <span class="n">dx</span><span class="p">(</span><span class="n">pj</span><span class="p">.</span><span class="n">x</span><span class="o">-</span><span class="n">pi</span><span class="p">.</span><span class="n">x</span><span class="p">),</span> <span class="n">dy</span><span class="p">(</span><span class="n">pj</span><span class="p">.</span><span class="n">y</span><span class="o">-</span><span class="n">pi</span><span class="p">.</span><span class="n">y</span><span class="p">)</span> <span class="p">{}</span> <span class="c1">// dy / dx &lt; l.dy / l.dx</span> <span class="kt">bool</span> <span class="k">operator</span> <span class="o">&lt;</span> <span class="p">(</span><span class="k">const</span> <span class="n">Line</span> <span class="o">&amp;</span><span class="n">l</span><span class="p">)</span> <span class="k">const</span> <span class="p">{</span> <span class="n">ll</span> <span class="n">le</span> <span class="o">=</span> <span class="n">dy</span> <span class="o">*</span> <span class="n">l</span><span class="p">.</span><span class="n">dx</span><span class="p">,</span> <span class="n">ri</span> <span class="o">=</span> <span class="n">l</span><span class="p">.</span><span class="n">dy</span> <span class="o">*</span> <span class="n">dx</span><span class="p">;</span> <span class="k">return</span> <span class="n">tie</span><span class="p">(</span><span class="n">le</span><span class="p">,</span> <span class="n">i</span><span class="p">,</span> <span class="n">j</span><span class="p">)</span> <span class="o">&lt;</span> <span class="n">tie</span><span class="p">(</span><span class="n">ri</span><span class="p">,</span> <span class="n">l</span><span class="p">.</span><span class="n">i</span><span class="p">,</span> <span class="n">l</span><span class="p">.</span><span class="n">j</span><span class="p">);</span> <span class="p">}</span> <span class="kt">bool</span> <span class="k">operator</span> <span class="o">==</span> <span class="p">(</span><span class="k">const</span> <span class="n">Line</span> <span class="o">&amp;</span><span class="n">l</span><span class="p">)</span> <span class="k">const</span> <span class="p">{</span> <span class="k">return</span> <span class="n">dy</span> <span class="o">*</span> <span class="n">l</span><span class="p">.</span><span class="n">dx</span> <span class="o">==</span> <span class="n">l</span><span class="p">.</span><span class="n">dy</span> <span class="o">*</span> <span class="n">dx</span><span class="p">;</span> <span class="p">}</span> <span class="p">};</span> <span class="n">ll</span> <span class="nf">TriangleArea</span><span class="p">(</span><span class="k">const</span> <span class="n">Point</span> <span class="o">&amp;</span><span class="n">p1</span><span class="p">,</span> <span class="k">const</span> <span class="n">Point</span> <span class="o">&amp;</span><span class="n">p2</span><span class="p">,</span> <span class="k">const</span> <span class="n">Point</span> <span class="o">&amp;</span><span class="n">p3</span><span class="p">){</span> <span class="n">ll</span> <span class="n">cross</span> <span class="o">=</span> <span class="p">(</span><span class="n">p2</span><span class="p">.</span><span class="n">x</span> <span class="o">-</span> <span class="n">p1</span><span class="p">.</span><span class="n">x</span><span class="p">)</span> <span class="o">*</span> <span class="p">(</span><span class="n">p3</span><span class="p">.</span><span class="n">y</span> <span class="o">-</span> <span class="n">p2</span><span class="p">.</span><span class="n">y</span><span class="p">)</span> <span class="o">-</span> <span class="p">(</span><span class="n">p3</span><span class="p">.</span><span class="n">x</span> <span class="o">-</span> <span class="n">p2</span><span class="p">.</span><span class="n">x</span><span class="p">)</span> <span class="o">*</span> <span class="p">(</span><span class="n">p2</span><span class="p">.</span><span class="n">y</span> <span class="o">-</span> <span class="n">p1</span><span class="p">.</span><span class="n">y</span><span class="p">);</span> <span class="k">return</span> <span class="n">abs</span><span class="p">(</span><span class="n">cross</span><span class="p">);</span> <span class="p">}</span> <span class="kt">int</span> <span class="n">N</span><span class="p">,</span> <span class="n">Pos</span><span class="p">[</span><span class="mi">2020</span><span class="p">];</span> <span class="n">Point</span> <span class="n">A</span><span class="p">[</span><span class="mi">2020</span><span class="p">];</span> <span class="kt">void</span> <span class="nf">Solve</span><span class="p">(){</span> <span class="n">sort</span><span class="p">(</span><span class="n">A</span><span class="o">+</span><span class="mi">1</span><span class="p">,</span> <span class="n">A</span><span class="o">+</span><span class="n">N</span><span class="o">+</span><span class="mi">1</span><span class="p">);</span> <span class="k">for</span><span class="p">(</span><span class="kt">int</span> <span class="n">i</span><span class="o">=</span><span class="mi">1</span><span class="p">;</span> <span class="n">i</span><span class="o">&lt;=</span><span class="n">N</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="n">Pos</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="n">i</span><span class="p">;</span> <span class="n">vector</span><span class="o">&lt;</span><span class="n">Line</span><span class="o">&gt;</span> <span class="n">V</span><span class="p">;</span> <span class="k">for</span><span class="p">(</span><span class="kt">int</span> <span class="n">i</span><span class="o">=</span><span class="mi">1</span><span class="p">;</span> <span class="n">i</span><span class="o">&lt;=</span><span class="n">N</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="k">for</span><span class="p">(</span><span class="kt">int</span> <span class="n">j</span><span class="o">=</span><span class="n">i</span><span class="o">+</span><span class="mi">1</span><span class="p">;</span> <span class="n">j</span><span class="o">&lt;=</span><span class="n">N</span><span class="p">;</span> <span class="n">j</span><span class="o">++</span><span class="p">)</span> <span class="n">V</span><span class="p">.</span><span class="n">emplace_back</span><span class="p">(</span><span class="n">i</span><span class="p">,</span> <span class="n">j</span><span class="p">,</span> <span class="n">A</span><span class="p">[</span><span class="n">i</span><span class="p">],</span> <span class="n">A</span><span class="p">[</span><span class="n">j</span><span class="p">]);</span> <span class="n">sort</span><span class="p">(</span><span class="n">V</span><span class="p">.</span><span class="n">begin</span><span class="p">(),</span> <span class="n">V</span><span class="p">.</span><span class="n">end</span><span class="p">());</span> <span class="n">ll</span> <span class="n">Min</span> <span class="o">=</span> <span class="mh">0x3f3f3f3f3f3f3f3f</span><span class="p">,</span> <span class="n">Max</span> <span class="o">=</span> <span class="mh">0xc0c0c0c0c0c0c0c0</span><span class="p">;</span> <span class="k">for</span><span class="p">(</span><span class="kt">int</span> <span class="n">i</span><span class="o">=</span><span class="mi">0</span><span class="p">,</span> <span class="n">j</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span> <span class="n">i</span><span class="o">&lt;</span><span class="n">V</span><span class="p">.</span><span class="n">size</span><span class="p">();</span> <span class="n">i</span><span class="o">=</span><span class="n">j</span><span class="p">){</span> <span class="k">while</span><span class="p">(</span><span class="n">j</span> <span class="o">&lt;</span> <span class="n">V</span><span class="p">.</span><span class="n">size</span><span class="p">()</span> <span class="o">&amp;&amp;</span> <span class="n">V</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">==</span> <span class="n">V</span><span class="p">[</span><span class="n">j</span><span class="p">])</span> <span class="n">j</span><span class="o">++</span><span class="p">;</span> <span class="c1">// [i, j) -&gt; same slope</span> <span class="k">for</span><span class="p">(</span><span class="kt">int</span> <span class="n">k</span><span class="o">=</span><span class="n">i</span><span class="p">;</span> <span class="n">k</span><span class="o">&lt;</span><span class="n">j</span><span class="p">;</span> <span class="n">k</span><span class="o">++</span><span class="p">){</span> <span class="kt">int</span> <span class="n">u</span> <span class="o">=</span> <span class="n">V</span><span class="p">[</span><span class="n">k</span><span class="p">].</span><span class="n">i</span><span class="p">,</span> <span class="n">v</span> <span class="o">=</span> <span class="n">V</span><span class="p">[</span><span class="n">k</span><span class="p">].</span><span class="n">j</span><span class="p">;</span> <span class="c1">// point id</span> <span class="n">swap</span><span class="p">(</span><span class="n">Pos</span><span class="p">[</span><span class="n">u</span><span class="p">],</span> <span class="n">Pos</span><span class="p">[</span><span class="n">v</span><span class="p">]);</span> <span class="n">swap</span><span class="p">(</span><span class="n">A</span><span class="p">[</span><span class="n">Pos</span><span class="p">[</span><span class="n">u</span><span class="p">]],</span> <span class="n">A</span><span class="p">[</span><span class="n">Pos</span><span class="p">[</span><span class="n">v</span><span class="p">]]);</span> <span class="k">if</span><span class="p">(</span><span class="n">Pos</span><span class="p">[</span><span class="n">u</span><span class="p">]</span> <span class="o">&gt;</span> <span class="n">Pos</span><span class="p">[</span><span class="n">v</span><span class="p">])</span> <span class="n">swap</span><span class="p">(</span><span class="n">u</span><span class="p">,</span> <span class="n">v</span><span class="p">);</span> <span class="k">if</span><span class="p">(</span><span class="n">Pos</span><span class="p">[</span><span class="n">u</span><span class="p">]</span> <span class="o">&gt;</span> <span class="mi">1</span><span class="p">){</span> <span class="n">Min</span> <span class="o">=</span> <span class="n">min</span><span class="p">(</span><span class="n">Min</span><span class="p">,</span> <span class="n">TriangleArea</span><span class="p">(</span><span class="n">A</span><span class="p">[</span><span class="n">Pos</span><span class="p">[</span><span class="n">u</span><span class="p">]],</span> <span class="n">A</span><span class="p">[</span><span class="n">Pos</span><span class="p">[</span><span class="n">v</span><span class="p">]],</span> <span class="n">A</span><span class="p">[</span><span class="n">Pos</span><span class="p">[</span><span class="n">u</span><span class="p">]</span><span class="o">-</span><span class="mi">1</span><span class="p">]));</span> <span class="n">Max</span> <span class="o">=</span> <span class="n">max</span><span class="p">(</span><span class="n">Max</span><span class="p">,</span> <span class="n">TriangleArea</span><span class="p">(</span><span class="n">A</span><span class="p">[</span><span class="n">Pos</span><span class="p">[</span><span class="n">u</span><span class="p">]],</span> <span class="n">A</span><span class="p">[</span><span class="n">Pos</span><span class="p">[</span><span class="n">v</span><span class="p">]],</span> <span class="n">A</span><span class="p">[</span><span class="mi">1</span><span class="p">]));</span> <span class="p">}</span> <span class="k">if</span><span class="p">(</span><span class="n">Pos</span><span class="p">[</span><span class="n">v</span><span class="p">]</span> <span class="o">&lt;</span> <span class="n">N</span><span class="p">){</span> <span class="n">Min</span> <span class="o">=</span> <span class="n">min</span><span class="p">(</span><span class="n">Min</span><span class="p">,</span> <span class="n">TriangleArea</span><span class="p">(</span><span class="n">A</span><span class="p">[</span><span class="n">Pos</span><span class="p">[</span><span class="n">u</span><span class="p">]],</span> <span class="n">A</span><span class="p">[</span><span class="n">Pos</span><span class="p">[</span><span class="n">v</span><span class="p">]],</span> <span class="n">A</span><span class="p">[</span><span class="n">Pos</span><span class="p">[</span><span class="n">v</span><span class="p">]</span><span class="o">+</span><span class="mi">1</span><span class="p">]));</span> <span class="n">Max</span> <span class="o">=</span> <span class="n">max</span><span class="p">(</span><span class="n">Max</span><span class="p">,</span> <span class="n">TriangleArea</span><span class="p">(</span><span class="n">A</span><span class="p">[</span><span class="n">Pos</span><span class="p">[</span><span class="n">u</span><span class="p">]],</span> <span class="n">A</span><span class="p">[</span><span class="n">Pos</span><span class="p">[</span><span class="n">v</span><span class="p">]],</span> <span class="n">A</span><span class="p">[</span><span class="n">N</span><span class="p">]));</span> <span class="p">}</span> <span class="p">}</span> <span class="p">}</span> <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">Min</span><span class="o">/</span><span class="mi">2</span> <span class="o">&lt;&lt;</span> <span class="s">"."</span> <span class="o">&lt;&lt;</span> <span class="n">Min</span><span class="o">%</span><span class="mi">2</span><span class="o">*</span><span class="mi">5</span> <span class="o">&lt;&lt;</span> <span class="s">" "</span><span class="p">;</span> <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">Max</span><span class="o">/</span><span class="mi">2</span> <span class="o">&lt;&lt;</span> <span class="s">"."</span> <span class="o">&lt;&lt;</span> <span class="n">Max</span><span class="o">%</span><span class="mi">2</span><span class="o">*</span><span class="mi">5</span> <span class="o">&lt;&lt;</span> <span class="s">"</span><span class="se">\n</span><span class="s">"</span><span class="p">;</span> <span class="p">}</span> <span class="kt">int</span> <span class="nf">main</span><span class="p">(){</span> <span class="n">ios_base</span><span class="o">::</span><span class="n">sync_with_stdio</span><span class="p">(</span><span class="nb">false</span><span class="p">);</span> <span class="n">cin</span><span class="p">.</span><span class="n">tie</span><span class="p">(</span><span class="nb">nullptr</span><span class="p">);</span> <span class="k">while</span><span class="p">(</span><span class="nb">true</span><span class="p">){</span> <span class="n">cin</span> <span class="o">&gt;&gt;</span> <span class="n">N</span><span class="p">;</span> <span class="k">if</span><span class="p">(</span><span class="o">!</span><span class="n">N</span><span class="p">)</span> <span class="k">break</span><span class="p">;</span> <span class="k">for</span><span class="p">(</span><span class="kt">int</span> <span class="n">i</span><span class="o">=</span><span class="mi">1</span><span class="p">;</span> <span class="n">i</span><span class="o">&lt;=</span><span class="n">N</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="n">cin</span> <span class="o">&gt;&gt;</span> <span class="n">A</span><span class="p">[</span><span class="n">i</span><span class="p">].</span><span class="n">x</span> <span class="o">&gt;&gt;</span> <span class="n">A</span><span class="p">[</span><span class="n">i</span><span class="p">].</span><span class="n">y</span><span class="p">;</span> <span class="n">Solve</span><span class="p">();</span> <span class="p">}</span> <span class="p">}</span> </code></pre></div></div> <h3 id="연습-문제">연습 문제</h3> <p><a href="https://www.acmicpc.net/workbook/view/3610">https://www.acmicpc.net/workbook/view/3610</a></p>JusticeHui서론 문제를 많이 풀어본 분들이라면 데이터가 정렬되어 있다는 것이 얼마나 좋은 성질인지 잘 알고 계실 것입니다. 이진 탐색을 이용할 수도 있고, 모든 원소가 서로 다른 배열에서 $A_i$보다 작은 가장 큰 원소와 같은 질의를 단순히 $A_{i-1}$을 반환하는 것으로 해결할 수 있습니다. 주어진 데이터가 정수와 같이 순서가 자명하게 정의되어 있다면 단순히 정렬하면 되지만, 2차원상의 점이라면 어떤 축을 기준으로 보느냐에 따라 정렬의 결과가 달라질 수 있습니다. 흔히 “불도저 트릭”이라고 불리는 이 테크닉은 서로 다른 정렬의 결과가 $O(N^2)$가지임을 보이고, 가능한 모든 정렬 결과를 $O(N^2 \log N)$에 순회하는 테크닉입니다.2022 숭고한 연합 알고리즘 대회 후기2022-03-27T14:00:00+00:002022-03-27T14:00:00+00:00https://justicehui.github.io/review/2022/03/27/skh<h2 id="서론">서론</h2> <p>3달 동안 준비한 숭고한 연합 알고리즘 대회가 드디어 끝났습니다. 저는 Div.1의 가장 어려운 문제, Div.2의 두 번째로 어려운 문제, Div.3의 가장 어려운 문제를 출제하고, 문제의 선제와 검수를 총괄했습니다. 대회 종료 후 풀이 방송도 담당했습니다.<br /> 올해 초에 대회 준비를 시작할 때부터 주변 지인들에게 때려치우고 싶다고 1시간에 한 번씩 징징거렸는데 대회가 끝나고 나니 속이 후련하네요.<br /> 운영진에 합류하게 된 과정부터 대회 풀이 방송까지, 여러 가지 이야기를 풀어보도록 하겠습니다.</p> <h2 id="운영진-합류">운영진 합류</h2> <p>원래 이 대회는 2월에 개최될 예정이었고, 저는 2월까지 1학년 신분이기 때문에 대회에 참가하려고 했습니다. 하지만 제가 운영하지 않으면 사람이 없어서 대회가 안 열린다는 이야기를 듣고 눈물을 흘리며 운영진에 합류하게 되었습니다.</p> <p>대회 코디네이터 역할을 맡은 것이라고 기대했지만 대회에 낼 문제가 부족해서 출제도 하게 되었습니다. 짧은 시간 동안 급하게 아이디어를 짜내다 보니 재미없는 문제만 2개 나왔습니다. 별로 대회에 내고 싶지 않았지만 어쩌다 보니 두 문제 모두 대회에 출제되었습니다.</p> <p>출제했다고 코디네이터를 안 해도 되는 건 아니었습니다. 운영진 중 대회 운영 경험이 가장 많았기 때문에 문제 선제와 검수를 총괄했습니다. 문제 선제 회의 진행을 도와주신 2021년 고려대학교 ALPS의 회장이자 BOJ 연말 대회의 핵심 운영진인 Ryute님과, 예산이 적어 검수비를 많이 드리지 못하는 상황임에도 기꺼이 검수를 도와주신 7명의 외부 검수진 덕분에 문제를 완성할 수 있었습니다.</p> <p>BOJ 측에 연락할 때 일개 운영진인 줄 알았던 제가 대회 개최자로 지정되어서 BOJ Stack도 제가 관리하게 되었습니다. 맨날 하는 일이라서 별로 부담이 되지 않았습니다.</p> <p>대회 풀이 방송도 제가 하게 되었고, 방송에서 사용할 문제 풀이 슬라이드 제작도 자연스럽게 제 담당이 되었습니다. 출제자가 풀이를 적어주면 제가 문장을 다듬고 내용을 적당히 수정해서 슬라이드에 넣었습니다.</p> <p>대회 문제와 관련된 대부분의 일을 제가 처리한 대신 행정적인 업무는 2021년 한양대학교 ALOHA 회장이신 dksu40님께서 담당해 주셨습니다. 정말 감사합니다.</p> <h2 id="문제-출제-과정---통행량-조사">문제 출제 과정 - 통행량 조사</h2> <p>Div.3의 마지막 문제(H)이자 Div.2의 G번 문제로 출제된 통행량 조사 문제는 제가 좋아하는 Unicycle Graph가 등장하는 문제입니다. 마침 한양대역이 있는 수도권 지하철 2호선이 Unicycle이라서 지문도 편하게 작성했습니다.</p> <p>원래 이 문제는 HLD를 활용한 $O(Q \log^2 N)$ 풀이를 막으려고 했지만 BOJ의 채점 서버가 너무 좋아서 실패했습니다. 분명 Codeforces Polygon에서는 막혔는데… Intel Xeon을 쓰는 BOJ에 비해 Polygon은 i3를 써서 캐시 메모리의 크기에서 차이가 발생하는 것 같습니다.</p> <p>이번 숭고한 연합 알고리즘 대회는 현대모비스의 후원을 받아 진행되었습니다. 기업을 소재로 한 문제를 만들어야 했고, 교통망과 관련된 이 문제가 후원 문제로 선정되었습니다. 덕분에 지문을 다시 썼습니다. 현대 오토에버의 후원을 받은 Hello, BOJ 2022!의 <a href="https://www.acmicpc.net/problem/24273">교통량 분석</a> 문제의 지문을 많이 참고했습니다.</p> <p>데이터는 Good Bye, BOJ 2020!에 출제했던 <a href="https://www.acmicpc.net/problem/20530">양분</a> 문제의 데이터를 재활용했습니다.</p> <p>아래 목록은 제가 작성한 솔루션 코드의 <strong>일부</strong>입니다. $O(N+Q)$, $O(N\log N+Q)$, $O(N+Q\log N)$, $O((N+Q)\log N)$, $O(N+Q\log^2 N)$ 등 다양한 복잡도의 풀이가 존재합니다.</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># O((N+Q) log N) ac-hui.cpp sparse table을 이용한 O(log N) LCA # O(N log N + Q) ac-fast-lca.cpp ac-hui에서 LCA를 전처리 O(N log N), 쿼리 O(1)에 처리 # O(N + Q) ac-linear.cpp ac-hui에서 LCA를 전처리 O(N), 쿼리 O(1)에 처리 # O(N + Q log N) ac-hld-lca.cpp ac-hui에서 LCA를 HLD로 구함 ac-hld-prefix.cpp HLD + Prefix Sum # O(N + Q log^2 N) slow-hld-seg.cpp HLD + Segment Tree slow-hld-fenwick.cpp HLD + Fenwick Tree slow-hld-lazy.cpp HLD + Segment Teee Lazy Propagation # TLE naive-dfs.cpp 백트래킹으로 경로 찾음 naive-path.cpp O(NQ), 간선 가중치를 naive하게 갱신 naive-perm.cpp O(QN!) </code></pre></div></div> <h2 id="문제-출제-과정---dcmsf">문제 출제 과정 - DCMSF</h2> <p>Div.1의 마지막 문제(H)로 출제된 DCMSF는 모르면 못 풀고 알면 쉽게 풀리는 전형적인 사전지식 문제라서 정말 대회에 내고 싶지 않았습니다. 다들 풀이를 먼저 고정해놓고 문제를 만들었다고 생각할 텐데, 사실 문제를 만들고 풀이를 생각한 문제입니다.</p> <p>2021 Winter SUAPC에 <a href="https://www.acmicpc.net/problem/20927">Degree Bounded Minimum Spanning Tree</a>라는 문제가 출제되었는데, 이 문제처럼 정점 차수에 제한이 있는 스패닝 트리를 구하는 것은 k = 2면 해밀턴 경로 문제로 reduction 되기 때문에 NP-Complete입니다. 여기에 조건을 적당히 붙여서 다항 시간에 풀 수 있는 문제를 만들고 싶었고, 이렇게 해서 나온 문제가 DCMSF입니다.</p> <p>처음에는 차수 제한이 1일 때 MST를 구하는 문제였습니다. 이건 왼쪽에 제한이 걸린 정점, 오른쪽에 나머지 정점을 놓고, 오른쪽에서 MST를 구한 다음에 Kuhn-munkres algorithm이나 MCMF 등으로 가중치 이분 매칭을 해서 해결할 수 있습니다.</p> <p>이틀 정도 문제를 고민하다 보니 트리 조건과 차수 조건이 각각 matroid라서 matroid intersection 으로도 문제를 해결할 수 있다는 것을 알게 되었습니다. 그리고 조금 더 고민해보니 트리 조건과 차수 1 제한 조건이 함께 있어도 matroid라서 단순한 그리디로 풀린다는 것을 깨달았습니다.<br /> 이렇게 문제를 버릴 수는 없어서 차수 $K_i$ 제한 조건을 추가해서 matroid intersection 문제로 만들었습니다.</p> <p>문제를 검수할 사람이 없었는데, 외부 검수진으로 참여하신 dennisstar님과 cs71107님에게 matroid를 강제로 공부시켜서 검수를 하게 했습니다. 정말 감사합니다.</p> <p>TMI) 일반적으로 3개 이상의 matroid intersection은 해밀턴 경로 문제로 reduction 할 수 있어서 다항 시간에 해결할 수 없습니다. 유향 그래프에서 간선 집합 $E$에서 $\mathcal{I}_1$을 모든 정점의 in-degree가 1 이하인 간선 집합, $\mathcal{I}_2$를 모든 정점의 out-degree가 1 이하인 간선 집합, $\mathcal{I_3}$를 방향을 무시했을 때 forest가 되는 간선 집합이라고 정의하면 해밀턴 경로 문제가 됩니다.</p> <h2 id="문제-검수">문제 검수</h2> <p>문제는 총 16문제였고 그중 제가 2문제를 출제했기 때문에 남은 14문제를 검수해야 하는 상황이었습니다. 문제별로 간단한 코멘트를 작성해 봅니다.</p> <p>Div.2는 완전 탐색과 그래프 이론, Div.1은 사전 지식 문제가 많이 나와서 셋의 밸런스가 좋지 않다는 것을 알고 있었지만, 대부분의 시간을 문제에 오류가 없도록 하는 것에 투자하느라 밸런스를 맞추려는 시도조차 하지 못했습니다. 이밖에도 전체적으로 문제 지문의 상태가 별로 좋지 않고 그냥 문제가 재미없다는 점 등 여러 이슈가 있었는데, 제가 별로 대회에 의욕이 없어서 그런지 알고 있음에도 불구하고 고치지 않았습니다. 여러 채널로 대회에 대한 피드백이 들어오고 있는데, 문제가 너무 안 좋다고 느껴졌다면 제 탓이니까 저를 욕하시면 됩니다.</p> <details> <summary><b>문제별 코멘트 (노잼 주의, 펼치기/닫기)</b></summary> <h4 id="div2a-">Div.2A 자동완성</h4> <p>현대모비스를 소재로 한 통행량 조사 문제처럼 Naver D2를 소재로 한 문제입니다. 저는 이 문제의 지문을 작성했습니다.</p> <h4 id="div2b--">Div.2B 장작 넣기</h4> <p>원래 Div.2D에 출제할 예정이었지만 다른 문제가 더 어려워서 앞으로 이동했습니다. 스코어보드를 보면 좋은 선택이었다고 생각합니다.</p> <p>문제를 잘 읽으면 완전 탐색을 이용해 간단하게 해결할 수 있습니다. 알고리즘을 처음 공부하는 분들은 대부분 완전 탐색을 구현하는 것에 익숙하지 않아서 많이 고전할 것이라 예상했지만, 이 점을 대회 4일 전에 깨달아서 손을 쓸 수 없었습니다. 죄송합니다.</p> <h4 id="div2c-">Div.2C 주식</h4> <p>대회 시작 30분 전에 풀이에 오류가 발견된 문제입니다. 기존에는 매수/매도의 양을 자유롭게 결정할 수 있고 대출도 최대 $K$배 만큼 자유롭게 할 수 있었는데, 급하게 영끌 대출/풀매수/풀매도로 수정했습니다. ahgus89님 정말 감사합니다.</p> <p>지문을 꼼꼼하게 읽지 않으면 많이 틀리기 좋은 문제고, 저도 검수하면서 많이 틀렸습니다. 장작 넣기처럼 완전 탐색을 이용해 해결할 수 있습니다.</p> <h4 id="div2d-skh-">Div.2D SKH 문자열</h4> <p>원래 Div.2B에 출제할 예정이었지만 너무 어려워서 장작 넣기와 위치를 바꿨습니다.</p> <h4 id="div2e--">Div.2E 최대한의 휴식</h4> <p>드디어 완전 탐색이 아닌 문제가 나왔습니다. 문제를 많이 풀어봤다면 기계적으로 풀 수 있는 문제고, 많이 안 풀어봤으면 어려운 문제인 것 같습니다. 처음에 지문을 이해하기 어려웠는데 다른 검수자들은 잘 푸는 것 같아서 딱히 수정하지는 않았습니다.</p> <h4 id="div2f--">Div.2F 노트 조각</h4> <p>교육적인 측면에서 봤을 때, Div.2에서 가장 좋은 문제라고 생각합니다. 이 문제도 문제를 많이 풀어봤다면 기계적으로 풀 수 있습니다. DE에 비해 전형적인 유형이라 그런지 문제 포지션에 비해 많이 풀렸습니다.</p> <p>원래 Bellman-Ford와 SPFA의 저격 데이터를 만들려고 했는데 귀찮아서 TLE 나와야 하는 코드만 올리고 방치했습니다. 결국 대회 4일 전에 출제자가 직접 저격 데이터를 만들었습니다.</p> <h4 id="div2g--">Div.2G 통행량 조사</h4> <p>노잼 문제</p> <h4 id="div2h--">Div.2H 원형 게임</h4> <p>원래 Good Bye, BOJ 2021 / Hello, BOJ 2022 후보 문제였는데 숭고한에 나오게 되었습니다. 제가 검수를 시작할 시점에는 이미 잘 완성되어 있어서 딱히 건든 부분이 없습니다.</p> <h4 id="div1a--">Div.1A 단어 마방진</h4> <p>원래 가지치기를 열심히 해야 하는 백트래킹 문제였지만, 올라온 솔루션마다 TLE 데이터가 튀어나오면서 단순한 완전 탐색 문제가 되었습니다.</p> <h4 id="div1b--">Div.1B 이차 함수</h4> <p>왠지 될 것 같은 풀이를 짜면 통과되는 문제입니다. Div.2에 나와도 될 난이도지만 모듈러 곱셈 역원 때문에 Div.1에 나오게 되었습니다. 다행히 Div.1에서는 페르마 소정리라는 지식이 걸림돌이 되지 않은 것 같습니다.</p> <p>고등학생 때 학교 공부를 안 했더니 풀이 증명하는 데 애를 먹었습니다. 아 미적분 너무 어려워…</p> <h4 id="div1c--">Div.1C 캐슬 디펜스</h4> <p>제가 검수를 시작할 시점에 이미 잘 완성되어 있어서 건들지 않았습니다.</p> <p>$ak-bt$가 최소가 되는 $(k, t)$를 2차원 좌표 평면에 찍으면 Convex Hull이 나오는 것을 이용해 새로운 문제를 만들 수 있을 것 같은데… 1시간 정도 고민했는데 좋은 아이디어가 안 떠올라서 포기했습니다.</p> <h4 id="div1d-">Div.1D 내적</h4> <p>식을 만지다 보면 전형적인 문제로 바뀝니다. 개인적으로 식을 잘 변형하는 것이 어려웠는데 많이 풀려서 놀랐습니다.</p> <p>오버플로우/실수 오차 때문에 좌표 범위를 더 키우지 못해 아쉬웠습니다.</p> <h4 id="div1e-">Div.1E 다트</h4> <p>원래 이 문제가 Div.1D에 나올 예정이었지만, 미리 작성된 코드 없이 이 문제를 푸는 것은 매우 어렵다고 생각해서 내적과 위치를 바꿨습니다. 사전에 작성한 코드가 있다면 두 문제의 난이도가 비슷합니다.</p> <p>2019 경기과학고 송년대회의 <a href="https://www.acmicpc.net/problem/18190">촛불과 그림자</a>의 하위 호환으로, 풀이를 떠올리기는 굉장히 쉽지만 구현이 고통스러운 기하 문제입니다. 2018 ICPC World Final 은메달리스트인 <a href="https://zigui.tistory.com/5">서울대학교 MolaMola팀의 팀노트</a> 덕분에 편하게 검수했습니다.</p> <h4 id="div1f---">Div.1F 르블랑의 트리 순회</h4> <p>검수 안 했습니다.</p> <h4 id="div1g--">Div.1G 숲 게임</h4> <p>BBST를 직접 구현해서 $O(N \log N)$ 솔루션을 작성하려고 했는데 귀찮아서 안 했습니다. 출제자가 의도한 풀이는 $O(N\log^2 N)$입니다.</p> <h4 id="div1h-dcmsf">Div.1H DCMSF</h4> <p>쓰레기 문제</p> </details> <h2 id="풀이-방송">풀이 방송</h2> <p>작년부터 알고리즘 강의를 비대면으로 하고 있기 때문에 마이크와 타블렛 같은 기본적인 장비는 이미 준비가 되어 있었습니다. 예전에 심심풀이로 문제 푸는 방송을 한 적이 있기 때문에 트위치 계정도 있고, OBS Studio도 사용할 수 있습니다. 그러다 보니 풀이 방송도 담당하게 되었습니다.</p> <p>풀이 슬라이드는 UCPC 2020에서 했던 것처럼 overleaf에서 제작했고, 문제 해설은 마찬가지로 UCPC 2020에서 했던 것처럼 drawable pdf를 사용했습니다. 대회 스폰서 중 하나인 CorcaAI의 홍보 세션은 Zoom 회의를 트위치로 송출하는 방식으로 진행했습니다.</p> <p>방송에 있어서 아쉬운 부분이 많이 있습니다. UCPC처럼 여러 레이아웃을 만들어서 깔끔하게 하고 싶었는데 제 능력과 시간이 모두 부족해서 단순히 화면 송출만 했습니다. 제가 검수하지 않은 Div1F와 Div1G의 풀이를 제대로 이해하지 못해서 잘 설명하지 못한 것도 아쉬웠습니다. 다음에 또 방송을 하게 된다면 방송 준비에 시간을 더 투자해야 할 것 같습니다. 그래도 슬라이드에 사소한 오타가 몇 개 있었던 것을 제외하면 별 문제 없이 진행된 점은 좋았습니다.</p> <h2 id="대회-마무리">대회 마무리</h2> <p>이제 상금 지급, 인건비 지급과 같은 일만 남아서 인건비 계산을 제외하면 제가 할 일은 없는 것 같습니다.</p> <p>대회 운영을 정말 밥 먹듯이 해왔지만, 이번처럼 운영진이 많은 대회에서 제가 중요한 역할을 맡은 건 처음이라 너무 힘들었습니다. 숭고한과는 비교도 안 될 만큼 규모가 큰 UCPC를 이끌어 나가는 UCPC 회장들과 BOJ 연말 대회 핵심 운영진들이 정말 대단하다는 것을 느꼈습니다.</p> <p>이번에 너무 많이 고생해서, SCCC 회장이지만 다음부터는 숭고한 대회 운영은 안 하고 싶습니다. 올해 여름에는 제 공부가 급해서 절대 운영 안 할 생각이고 겨울에는 내년 회장한테 넘기고 도망가는 것이 목표입니다.<br /> <strong>“알고리즘 동아리들이 모여 공부 방법과 내용을 교류하자”</strong> 는 취지에서 만들어진 행사인 만큼 차라리 알고리즘 강의를 시키면 할 텐데, 다른 운영진들은 강의보다 대회를 더 중요하게 생각하는 것 같아 아쉽습니다. 강의 준비보다 대회 개최가 더 쉽다고 생각하는 사람들이 많던데, 대회 개최를 가볍게 생각하지 않았으면 좋겠습니다.</p> <p>고등학생 때 고생했던 것들이 지금은 정말 재미있었던 기억으로 남아있는 걸 생각해보면, 나중에 생각해봤을 때 이것도 나름 재미있던 추억으로 남을지도 모르겠습니다. 심지어 대회 준비 초반에 화났던 일들이 지금은 잘 기억이 나지 않습니다. <strong>하지만</strong> 이번에 힘들었던 기억을 까먹고 다시 숭고한 대회를 운영하는 일은 발생하지 않았으면 좋겠습니다. 제 기억력을 한 번 믿어보겠습니다.</p> <h2 id="마치며">마치며…</h2> <p>고등학교 2학년 때 대회 열어보겠다고 객기부리던 것과 3학년 때 UCPC Call for Tasks에 문제 제출하고 조마조마하며 기다리던 것이 엊그제 같은데, 이제는 문제 출제와 관련된 대부분의 업무를 처리할 수 있을 정도로 성장한 것 같습니다. 3년 전, 수능을 100일 앞둔 시점에 제 첫 대회 개최를 도와준 Ryute님과 UCPC, SPC, SUAPC 등 다양한 대회의 운영 경력을 쌓을 기회를 주신 shiftpsh님을 비롯한 서강대 분들, BOJ 연말 대회마다 저를 불러주신 leejseo님, 그리고 최고의 선배 wookje님께 정말 감사드립니다.</p> <p>중간고사 이후부터는 SCCC 내부 스터디를 진행해야 해서 쉴 틈이 없을 것 같습니다. 소모임 회장을 괜히 한 것 같아 매일 후회하고 있지만, 미래의 저에게는 좋은 추억과 양분이 되리라 생각하며 열심히 하고 있습니다.</p> <p>학점을 포기했다고 떠드는 사람은 대외 활동을 지배하고 있다는 말이 있습니다. 이미 학점은 망해서 대외 활동을 지배해볼까 합니다. 뭔가 한 건 많은데 수상 실적이 부족한 것 같으니 SCPC와 ICPC를 위해 열심히 공부해야겠습니다. 여름방학 끝날 때까지 대회 운영 안 한다는 얘기입니다.</p> <p>끝!</p>JusticeHui서론 3달 동안 준비한 숭고한 연합 알고리즘 대회가 드디어 끝났습니다. 저는 Div.1의 가장 어려운 문제, Div.2의 두 번째로 어려운 문제, Div.3의 가장 어려운 문제를 출제하고, 문제의 선제와 검수를 총괄했습니다. 대회 종료 후 풀이 방송도 담당했습니다. 올해 초에 대회 준비를 시작할 때부터 주변 지인들에게 때려치우고 싶다고 1시간에 한 번씩 징징거렸는데 대회가 끝나고 나니 속이 후련하네요. 운영진에 합류하게 된 과정부터 대회 풀이 방송까지, 여러 가지 이야기를 풀어보도록 하겠습니다.2022 성균관대학교 프로그래밍 경진대회 검수와 상수 커팅 이야기2022-02-27T10:51:00+00:002022-02-27T10:51:00+00:00https://justicehui.github.io/ps/2022/02/27/2022skku<h2 id="서론">서론</h2> <p>2022년 성균관대학교 프로그래밍 경진대회에 검수자로 참여했습니다.<br /> 이번 대회에는 저보다 훨씬 실력이 좋은 분들이 검수자로 함께 참여했습니다. 다른 검수자들이 저보다 훨씬 빠르고 정확하게 풀이 검증을 해주셨기 때문에 저는 상수 최적화를 통한 데이터 강화에 더 집중했습니다. 평소에는 대회 검수 후기를 잘 남기지 않는 편이지만, 이번 검수에서 시도한 상수 최적화 방법들이 인상적이라서 오랜만에 검수 후기를 작성해봅니다.<br /> 캐시 히트, 분기 예측, SIMD를 알고 있으면 글을 읽는데 도움이 될 수 있습니다.</p> <h2 id="boj-24529-이야기-배열---문제-설명">BOJ 24529 이야기 배열 - 문제 설명</h2> <blockquote> <p>3개의 이야기 보따리에 각각 $N$개의 이야기가 있다. 이야기는 길이와 재미있는 정도를 갖고 있다.<br />$3N$의 이야기 중 $N$개의 이야기를 뽑아 나열해서 <strong>재미있는 정도의 합을 최대화</strong>해야 하는데, 이때 $i$번째로 오는 <strong>이야기의 길이는 $D_i$ 이하</strong>가 되어야 한다.<br />$D_i$가 단조 증가할 때($D_i \leq D_{i+1}$), 이야기의 재미있는 정도의 합의 최댓값을 구하자.</p> </blockquote> <p>문제 풀이를 먼저 설명하겠습니다.<br /> 이야기 길이의 상한 조건을 만족하는 임의의 이야기 배열은, 같은 보따리에서 나온 이야기를 길이가 단조 증가하는 형태로 바꿀 수 있습니다. 그러므로 각 보따리에 있는 이야기를 길이 오름차순으로 정렬한 뒤 순서대로 뽑아도 됩니다. 이 아이디어를 이용하면 아래와 같은 점화식을 쉽게 떠올릴 수 있습니다.<br /> $D[n][a][b][c][p] := $ 세 개의 보따리에서 각각 $a, b, c$ 번째 이야기까지 사용해서 $n$개의 이야기를 선택했고, 마지막으로 선택한 보따리가 $p$일 때 재미의 최댓값</p> <p>이 점화식은 $O(N^4)$개의 상태가 있고, 각 상태의 답을 $O(1)$에 계산할 수 있기 때문에 $O(N^4)$에 문제를 해결할 수 있습니다. 이것이 출제자가 의도한 풀이지만, 열심히 최적화를 하면 $O(N^5)$로도 문제를 해결할 수 있습니다.</p> <h2 id="boj-24529-이야기-배열---on5-최적화">BOJ 24529 이야기 배열 - $O(N^5)$ 최적화</h2> <h4 id="시작">시작</h4> <p>가장 기본적인 코드는 다음과 같습니다. <code class="language-plaintext highlighter-rouge">GetDP</code> 함수를 $O(N^4)$번 호출하고, <code class="language-plaintext highlighter-rouge">GetDP</code> 함수는 상태 3개의 답을 $O(N)$에 계산하기 때문에 $O(N^5)$입니다. 이 코드의 실행 시간은 <strong>2400ms</strong> 입니다.</p> <div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include &lt;bits/stdc++.h&gt; </span><span class="k">using</span> <span class="k">namespace</span> <span class="n">std</span><span class="p">;</span> <span class="kt">int</span> <span class="n">N</span><span class="p">,</span> <span class="n">L</span><span class="p">[</span><span class="mi">64</span><span class="p">],</span> <span class="n">D</span><span class="p">[</span><span class="mi">64</span><span class="p">][</span><span class="mi">64</span><span class="p">][</span><span class="mi">64</span><span class="p">][</span><span class="mi">64</span><span class="p">][</span><span class="mi">3</span><span class="p">];</span> <span class="n">pair</span><span class="o">&lt;</span><span class="kt">int</span><span class="p">,</span><span class="kt">int</span><span class="o">&gt;</span> <span class="n">A</span><span class="p">[</span><span class="mi">64</span><span class="p">],</span> <span class="n">B</span><span class="p">[</span><span class="mi">64</span><span class="p">],</span> <span class="n">C</span><span class="p">[</span><span class="mi">64</span><span class="p">];</span> <span class="kt">void</span> <span class="nf">GetDP</span><span class="p">(</span><span class="kt">int</span> <span class="n">i</span><span class="p">,</span> <span class="kt">int</span> <span class="n">a</span><span class="p">,</span> <span class="kt">int</span> <span class="n">b</span><span class="p">,</span> <span class="kt">int</span> <span class="n">c</span><span class="p">){</span> <span class="k">if</span><span class="p">(</span><span class="n">A</span><span class="p">[</span><span class="n">a</span><span class="p">].</span><span class="n">first</span> <span class="o">&lt;=</span> <span class="n">L</span><span class="p">[</span><span class="n">i</span><span class="p">]){</span> <span class="kt">int</span> <span class="o">&amp;</span><span class="n">now</span> <span class="o">=</span> <span class="n">D</span><span class="p">[</span><span class="n">i</span><span class="p">][</span><span class="n">a</span><span class="p">][</span><span class="n">b</span><span class="p">][</span><span class="n">c</span><span class="p">][</span><span class="mi">0</span><span class="p">];</span> <span class="k">for</span><span class="p">(</span><span class="kt">int</span> <span class="n">j</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span> <span class="n">j</span><span class="o">&lt;</span><span class="n">a</span><span class="p">;</span> <span class="n">j</span><span class="o">++</span><span class="p">){</span> <span class="k">if</span><span class="p">(</span><span class="n">D</span><span class="p">[</span><span class="n">i</span><span class="o">-</span><span class="mi">1</span><span class="p">][</span><span class="n">j</span><span class="p">][</span><span class="n">b</span><span class="p">][</span><span class="n">c</span><span class="p">][</span><span class="mi">1</span><span class="p">]</span> <span class="o">!=</span> <span class="o">-</span><span class="mi">1</span><span class="p">)</span> <span class="n">now</span> <span class="o">=</span> <span class="n">max</span><span class="p">(</span><span class="n">now</span><span class="p">,</span> <span class="n">D</span><span class="p">[</span><span class="n">i</span><span class="o">-</span><span class="mi">1</span><span class="p">][</span><span class="n">j</span><span class="p">][</span><span class="n">b</span><span class="p">][</span><span class="n">c</span><span class="p">][</span><span class="mi">1</span><span class="p">]</span> <span class="o">+</span> <span class="n">A</span><span class="p">[</span><span class="n">a</span><span class="p">].</span><span class="n">second</span><span class="p">);</span> <span class="k">if</span><span class="p">(</span><span class="n">D</span><span class="p">[</span><span class="n">i</span><span class="o">-</span><span class="mi">1</span><span class="p">][</span><span class="n">j</span><span class="p">][</span><span class="n">b</span><span class="p">][</span><span class="n">c</span><span class="p">][</span><span class="mi">2</span><span class="p">]</span> <span class="o">!=</span> <span class="o">-</span><span class="mi">1</span><span class="p">)</span> <span class="n">now</span> <span class="o">=</span> <span class="n">max</span><span class="p">(</span><span class="n">now</span><span class="p">,</span> <span class="n">D</span><span class="p">[</span><span class="n">i</span><span class="o">-</span><span class="mi">1</span><span class="p">][</span><span class="n">j</span><span class="p">][</span><span class="n">b</span><span class="p">][</span><span class="n">c</span><span class="p">][</span><span class="mi">2</span><span class="p">]</span> <span class="o">+</span> <span class="n">A</span><span class="p">[</span><span class="n">a</span><span class="p">].</span><span class="n">second</span><span class="p">);</span> <span class="p">}</span> <span class="p">}</span> <span class="k">if</span><span class="p">(</span><span class="n">B</span><span class="p">[</span><span class="n">b</span><span class="p">].</span><span class="n">first</span> <span class="o">&lt;=</span> <span class="n">L</span><span class="p">[</span><span class="n">i</span><span class="p">]){</span> <span class="kt">int</span> <span class="o">&amp;</span><span class="n">now</span> <span class="o">=</span> <span class="n">D</span><span class="p">[</span><span class="n">i</span><span class="p">][</span><span class="n">a</span><span class="p">][</span><span class="n">b</span><span class="p">][</span><span class="n">c</span><span class="p">][</span><span class="mi">1</span><span class="p">];</span> <span class="k">for</span><span class="p">(</span><span class="kt">int</span> <span class="n">j</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span> <span class="n">j</span><span class="o">&lt;</span><span class="n">b</span><span class="p">;</span> <span class="n">j</span><span class="o">++</span><span class="p">){</span> <span class="k">if</span><span class="p">(</span><span class="n">D</span><span class="p">[</span><span class="n">i</span><span class="o">-</span><span class="mi">1</span><span class="p">][</span><span class="n">a</span><span class="p">][</span><span class="n">j</span><span class="p">][</span><span class="n">c</span><span class="p">][</span><span class="mi">0</span><span class="p">]</span> <span class="o">!=</span> <span class="o">-</span><span class="mi">1</span><span class="p">)</span> <span class="n">now</span> <span class="o">=</span> <span class="n">max</span><span class="p">(</span><span class="n">now</span><span class="p">,</span> <span class="n">D</span><span class="p">[</span><span class="n">i</span><span class="o">-</span><span class="mi">1</span><span class="p">][</span><span class="n">a</span><span class="p">][</span><span class="n">j</span><span class="p">][</span><span class="n">c</span><span class="p">][</span><span class="mi">0</span><span class="p">]</span> <span class="o">+</span> <span class="n">B</span><span class="p">[</span><span class="n">b</span><span class="p">].</span><span class="n">second</span><span class="p">);</span> <span class="k">if</span><span class="p">(</span><span class="n">D</span><span class="p">[</span><span class="n">i</span><span class="o">-</span><span class="mi">1</span><span class="p">][</span><span class="n">a</span><span class="p">][</span><span class="n">j</span><span class="p">][</span><span class="n">c</span><span class="p">][</span><span class="mi">2</span><span class="p">]</span> <span class="o">!=</span> <span class="o">-</span><span class="mi">1</span><span class="p">)</span> <span class="n">now</span> <span class="o">=</span> <span class="n">max</span><span class="p">(</span><span class="n">now</span><span class="p">,</span> <span class="n">D</span><span class="p">[</span><span class="n">i</span><span class="o">-</span><span class="mi">1</span><span class="p">][</span><span class="n">a</span><span class="p">][</span><span class="n">j</span><span class="p">][</span><span class="n">c</span><span class="p">][</span><span class="mi">2</span><span class="p">]</span> <span class="o">+</span> <span class="n">B</span><span class="p">[</span><span class="n">b</span><span class="p">].</span><span class="n">second</span><span class="p">);</span> <span class="p">}</span> <span class="p">}</span> <span class="k">if</span><span class="p">(</span><span class="n">C</span><span class="p">[</span><span class="n">c</span><span class="p">].</span><span class="n">first</span> <span class="o">&lt;=</span> <span class="n">L</span><span class="p">[</span><span class="n">i</span><span class="p">]){</span> <span class="kt">int</span> <span class="o">&amp;</span><span class="n">now</span> <span class="o">=</span> <span class="n">D</span><span class="p">[</span><span class="n">i</span><span class="p">][</span><span class="n">a</span><span class="p">][</span><span class="n">b</span><span class="p">][</span><span class="n">c</span><span class="p">][</span><span class="mi">2</span><span class="p">];</span> <span class="k">for</span><span class="p">(</span><span class="kt">int</span> <span class="n">j</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span> <span class="n">j</span><span class="o">&lt;</span><span class="n">c</span><span class="p">;</span> <span class="n">j</span><span class="o">++</span><span class="p">){</span> <span class="k">if</span><span class="p">(</span><span class="n">D</span><span class="p">[</span><span class="n">i</span><span class="o">-</span><span class="mi">1</span><span class="p">][</span><span class="n">a</span><span class="p">][</span><span class="n">b</span><span class="p">][</span><span class="n">j</span><span class="p">][</span><span class="mi">0</span><span class="p">]</span> <span class="o">!=</span> <span class="o">-</span><span class="mi">1</span><span class="p">)</span> <span class="n">now</span> <span class="o">=</span> <span class="n">max</span><span class="p">(</span><span class="n">now</span><span class="p">,</span> <span class="n">D</span><span class="p">[</span><span class="n">i</span><span class="o">-</span><span class="mi">1</span><span class="p">][</span><span class="n">a</span><span class="p">][</span><span class="n">b</span><span class="p">][</span><span class="n">j</span><span class="p">][</span><span class="mi">0</span><span class="p">]</span> <span class="o">+</span> <span class="n">C</span><span class="p">[</span><span class="n">c</span><span class="p">].</span><span class="n">second</span><span class="p">);</span> <span class="k">if</span><span class="p">(</span><span class="n">D</span><span class="p">[</span><span class="n">i</span><span class="o">-</span><span class="mi">1</span><span class="p">][</span><span class="n">a</span><span class="p">][</span><span class="n">b</span><span class="p">][</span><span class="n">j</span><span class="p">][</span><span class="mi">1</span><span class="p">]</span> <span class="o">!=</span> <span class="o">-</span><span class="mi">1</span><span class="p">)</span> <span class="n">now</span> <span class="o">=</span> <span class="n">max</span><span class="p">(</span><span class="n">now</span><span class="p">,</span> <span class="n">D</span><span class="p">[</span><span class="n">i</span><span class="o">-</span><span class="mi">1</span><span class="p">][</span><span class="n">a</span><span class="p">][</span><span class="n">b</span><span class="p">][</span><span class="n">j</span><span class="p">][</span><span class="mi">1</span><span class="p">]</span> <span class="o">+</span> <span class="n">C</span><span class="p">[</span><span class="n">c</span><span class="p">].</span><span class="n">second</span><span class="p">);</span> <span class="p">}</span> <span class="p">}</span> <span class="p">}</span> <span class="kt">int</span> <span class="nf">main</span><span class="p">(){</span> <span class="n">ios_base</span><span class="o">::</span><span class="n">sync_with_stdio</span><span class="p">(</span><span class="nb">false</span><span class="p">);</span> <span class="n">cin</span><span class="p">.</span><span class="n">tie</span><span class="p">(</span><span class="nb">nullptr</span><span class="p">);</span> <span class="n">cin</span> <span class="o">&gt;&gt;</span> <span class="n">N</span><span class="p">;</span> <span class="k">for</span><span class="p">(</span><span class="kt">int</span> <span class="n">i</span><span class="o">=</span><span class="mi">1</span><span class="p">;</span> <span class="n">i</span><span class="o">&lt;=</span><span class="n">N</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="n">cin</span> <span class="o">&gt;&gt;</span> <span class="n">A</span><span class="p">[</span><span class="n">i</span><span class="p">].</span><span class="n">second</span> <span class="o">&gt;&gt;</span> <span class="n">A</span><span class="p">[</span><span class="n">i</span><span class="p">].</span><span class="n">first</span><span class="p">;</span> <span class="k">for</span><span class="p">(</span><span class="kt">int</span> <span class="n">i</span><span class="o">=</span><span class="mi">1</span><span class="p">;</span> <span class="n">i</span><span class="o">&lt;=</span><span class="n">N</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="n">cin</span> <span class="o">&gt;&gt;</span> <span class="n">B</span><span class="p">[</span><span class="n">i</span><span class="p">].</span><span class="n">second</span> <span class="o">&gt;&gt;</span> <span class="n">B</span><span class="p">[</span><span class="n">i</span><span class="p">].</span><span class="n">first</span><span class="p">;</span> <span class="k">for</span><span class="p">(</span><span class="kt">int</span> <span class="n">i</span><span class="o">=</span><span class="mi">1</span><span class="p">;</span> <span class="n">i</span><span class="o">&lt;=</span><span class="n">N</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="n">cin</span> <span class="o">&gt;&gt;</span> <span class="n">C</span><span class="p">[</span><span class="n">i</span><span class="p">].</span><span class="n">second</span> <span class="o">&gt;&gt;</span> <span class="n">C</span><span class="p">[</span><span class="n">i</span><span class="p">].</span><span class="n">first</span><span class="p">;</span> <span class="k">for</span><span class="p">(</span><span class="kt">int</span> <span class="n">i</span><span class="o">=</span><span class="mi">1</span><span class="p">;</span> <span class="n">i</span><span class="o">&lt;=</span><span class="n">N</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="n">cin</span> <span class="o">&gt;&gt;</span> <span class="n">L</span><span class="p">[</span><span class="n">i</span><span class="p">];</span> <span class="n">sort</span><span class="p">(</span><span class="n">A</span><span class="o">+</span><span class="mi">1</span><span class="p">,</span> <span class="n">A</span><span class="o">+</span><span class="n">N</span><span class="o">+</span><span class="mi">1</span><span class="p">);</span> <span class="n">sort</span><span class="p">(</span><span class="n">B</span><span class="o">+</span><span class="mi">1</span><span class="p">,</span> <span class="n">B</span><span class="o">+</span><span class="n">N</span><span class="o">+</span><span class="mi">1</span><span class="p">);</span> <span class="n">sort</span><span class="p">(</span><span class="n">C</span><span class="o">+</span><span class="mi">1</span><span class="p">,</span> <span class="n">C</span><span class="o">+</span><span class="n">N</span><span class="o">+</span><span class="mi">1</span><span class="p">);</span> <span class="n">memset</span><span class="p">(</span><span class="n">D</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="k">sizeof</span> <span class="n">D</span><span class="p">);</span> <span class="k">for</span><span class="p">(</span><span class="kt">int</span> <span class="n">i</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span> <span class="n">i</span><span class="o">&lt;</span><span class="mi">3</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="n">D</span><span class="p">[</span><span class="mi">0</span><span class="p">][</span><span class="mi">0</span><span class="p">][</span><span class="mi">0</span><span class="p">][</span><span class="mi">0</span><span class="p">][</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="k">for</span><span class="p">(</span><span class="kt">int</span> <span class="n">i</span><span class="o">=</span><span class="mi">1</span><span class="p">;</span> <span class="n">i</span><span class="o">&lt;=</span><span class="n">N</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">){</span> <span class="k">for</span><span class="p">(</span><span class="kt">int</span> <span class="n">a</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span> <span class="n">a</span><span class="o">&lt;=</span><span class="n">N</span><span class="p">;</span> <span class="n">a</span><span class="o">++</span><span class="p">)</span> <span class="k">for</span><span class="p">(</span><span class="kt">int</span> <span class="n">b</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span> <span class="n">b</span><span class="o">&lt;=</span><span class="n">N</span><span class="p">;</span> <span class="n">b</span><span class="o">++</span><span class="p">)</span> <span class="k">for</span><span class="p">(</span><span class="kt">int</span> <span class="n">c</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span> <span class="n">c</span><span class="o">&lt;=</span><span class="n">N</span><span class="p">;</span> <span class="n">c</span><span class="o">++</span><span class="p">)</span> <span class="n">GetDP</span><span class="p">(</span><span class="n">i</span><span class="p">,</span> <span class="n">a</span><span class="p">,</span> <span class="n">b</span><span class="p">,</span> <span class="n">c</span><span class="p">);</span> <span class="p">}</span> <span class="kt">int</span> <span class="n">mx</span> <span class="o">=</span> <span class="o">-</span><span class="mi">1</span><span class="p">;</span> <span class="k">for</span><span class="p">(</span><span class="kt">int</span> <span class="n">a</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span> <span class="n">a</span><span class="o">&lt;=</span><span class="n">N</span><span class="p">;</span> <span class="n">a</span><span class="o">++</span><span class="p">)</span> <span class="k">for</span><span class="p">(</span><span class="kt">int</span> <span class="n">b</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span> <span class="n">b</span><span class="o">&lt;=</span><span class="n">N</span><span class="p">;</span> <span class="n">b</span><span class="o">++</span><span class="p">)</span> <span class="k">for</span><span class="p">(</span><span class="kt">int</span> <span class="n">c</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span> <span class="n">c</span><span class="o">&lt;=</span><span class="n">N</span><span class="p">;</span> <span class="n">c</span><span class="o">++</span><span class="p">){</span> <span class="k">for</span><span class="p">(</span><span class="kt">int</span> <span class="n">d</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span> <span class="n">d</span><span class="o">&lt;</span><span class="mi">3</span><span class="p">;</span> <span class="n">d</span><span class="o">++</span><span class="p">)</span> <span class="n">mx</span> <span class="o">=</span> <span class="n">max</span><span class="p">(</span><span class="n">mx</span><span class="p">,</span> <span class="n">D</span><span class="p">[</span><span class="n">N</span><span class="p">][</span><span class="n">a</span><span class="p">][</span><span class="n">b</span><span class="p">][</span><span class="n">c</span><span class="p">][</span><span class="n">d</span><span class="p">]);</span> <span class="p">}</span> <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">mx</span><span class="p">;</span> <span class="p">}</span> </code></pre></div></div> <h4 id="캐시-히트">캐시 히트</h4> <p>사용하는 메모리의 크기가 커질수록 캐시 미스가 더 많이 발생하기 때문에 DP 테이블의 크기를 줄여야 합니다. <code class="language-plaintext highlighter-rouge">GetDP</code> 함수에서 $D[i]$의 값을 계산할 때 $D[i-1]$만 사용하므로 토글링을 사용해서 DP 테이블의 크기를 $N\cdot N\cdot N\cdot N\cdot 3$에서 $2\cdot N\cdot N\cdot N\cdot 3$으로 줄일 수 있습니다.<br /> <code class="language-plaintext highlighter-rouge">GetDP</code> 함수에서 모든 <code class="language-plaintext highlighter-rouge">i</code>와 <code class="language-plaintext highlighter-rouge">i-1</code> 뒤에 <code class="language-plaintext highlighter-rouge">&amp;1</code>이 붙었으며, 47번째 줄에 <code class="language-plaintext highlighter-rouge">memset(D[i&amp;1], -1, sizeof(D) / 2);</code>이 추가되었습니다. 이 코드의 실행 시간은 <strong>2100ms</strong> 이며, 이전 코드에 비해 300ms 정도 감소했습니다.</p> <div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include &lt;bits/stdc++.h&gt; </span><span class="k">using</span> <span class="k">namespace</span> <span class="n">std</span><span class="p">;</span> <span class="kt">int</span> <span class="n">N</span><span class="p">,</span> <span class="n">L</span><span class="p">[</span><span class="mi">64</span><span class="p">],</span> <span class="n">D</span><span class="p">[</span><span class="mi">2</span><span class="p">][</span><span class="mi">64</span><span class="p">][</span><span class="mi">64</span><span class="p">][</span><span class="mi">64</span><span class="p">][</span><span class="mi">3</span><span class="p">];</span> <span class="n">pair</span><span class="o">&lt;</span><span class="kt">int</span><span class="p">,</span><span class="kt">int</span><span class="o">&gt;</span> <span class="n">A</span><span class="p">[</span><span class="mi">64</span><span class="p">],</span> <span class="n">B</span><span class="p">[</span><span class="mi">64</span><span class="p">],</span> <span class="n">C</span><span class="p">[</span><span class="mi">64</span><span class="p">];</span> <span class="kt">void</span> <span class="nf">GetDP</span><span class="p">(</span><span class="kt">int</span> <span class="n">i</span><span class="p">,</span> <span class="kt">int</span> <span class="n">a</span><span class="p">,</span> <span class="kt">int</span> <span class="n">b</span><span class="p">,</span> <span class="kt">int</span> <span class="n">c</span><span class="p">){</span> <span class="k">if</span><span class="p">(</span><span class="n">A</span><span class="p">[</span><span class="n">a</span><span class="p">].</span><span class="n">first</span> <span class="o">&lt;=</span> <span class="n">L</span><span class="p">[</span><span class="n">i</span><span class="p">]){</span> <span class="kt">int</span> <span class="o">&amp;</span><span class="n">now</span> <span class="o">=</span> <span class="n">D</span><span class="p">[</span><span class="n">i</span><span class="o">&amp;</span><span class="mi">1</span><span class="p">][</span><span class="n">a</span><span class="p">][</span><span class="n">b</span><span class="p">][</span><span class="n">c</span><span class="p">][</span><span class="mi">0</span><span class="p">];</span> <span class="k">for</span><span class="p">(</span><span class="kt">int</span> <span class="n">j</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span> <span class="n">j</span><span class="o">&lt;</span><span class="n">a</span><span class="p">;</span> <span class="n">j</span><span class="o">++</span><span class="p">){</span> <span class="k">if</span><span class="p">(</span><span class="n">D</span><span class="p">[</span><span class="n">i</span><span class="o">-</span><span class="mi">1</span><span class="o">&amp;</span><span class="mi">1</span><span class="p">][</span><span class="n">j</span><span class="p">][</span><span class="n">b</span><span class="p">][</span><span class="n">c</span><span class="p">][</span><span class="mi">1</span><span class="p">]</span> <span class="o">!=</span> <span class="o">-</span><span class="mi">1</span><span class="p">)</span> <span class="n">now</span> <span class="o">=</span> <span class="n">max</span><span class="p">(</span><span class="n">now</span><span class="p">,</span> <span class="n">D</span><span class="p">[</span><span class="n">i</span><span class="o">-</span><span class="mi">1</span><span class="o">&amp;</span><span class="mi">1</span><span class="p">][</span><span class="n">j</span><span class="p">][</span><span class="n">b</span><span class="p">][</span><span class="n">c</span><span class="p">][</span><span class="mi">1</span><span class="p">]</span> <span class="o">+</span> <span class="n">A</span><span class="p">[</span><span class="n">a</span><span class="p">].</span><span class="n">second</span><span class="p">);</span> <span class="k">if</span><span class="p">(</span><span class="n">D</span><span class="p">[</span><span class="n">i</span><span class="o">-</span><span class="mi">1</span><span class="o">&amp;</span><span class="mi">1</span><span class="p">][</span><span class="n">j</span><span class="p">][</span><span class="n">b</span><span class="p">][</span><span class="n">c</span><span class="p">][</span><span class="mi">2</span><span class="p">]</span> <span class="o">!=</span> <span class="o">-</span><span class="mi">1</span><span class="p">)</span> <span class="n">now</span> <span class="o">=</span> <span class="n">max</span><span class="p">(</span><span class="n">now</span><span class="p">,</span> <span class="n">D</span><span class="p">[</span><span class="n">i</span><span class="o">-</span><span class="mi">1</span><span class="o">&amp;</span><span class="mi">1</span><span class="p">][</span><span class="n">j</span><span class="p">][</span><span class="n">b</span><span class="p">][</span><span class="n">c</span><span class="p">][</span><span class="mi">2</span><span class="p">]</span> <span class="o">+</span> <span class="n">A</span><span class="p">[</span><span class="n">a</span><span class="p">].</span><span class="n">second</span><span class="p">);</span> <span class="p">}</span> <span class="p">}</span> <span class="k">if</span><span class="p">(</span><span class="n">B</span><span class="p">[</span><span class="n">b</span><span class="p">].</span><span class="n">first</span> <span class="o">&lt;=</span> <span class="n">L</span><span class="p">[</span><span class="n">i</span><span class="p">]){</span> <span class="kt">int</span> <span class="o">&amp;</span><span class="n">now</span> <span class="o">=</span> <span class="n">D</span><span class="p">[</span><span class="n">i</span><span class="o">&amp;</span><span class="mi">1</span><span class="p">][</span><span class="n">a</span><span class="p">][</span><span class="n">b</span><span class="p">][</span><span class="n">c</span><span class="p">][</span><span class="mi">1</span><span class="p">];</span> <span class="k">for</span><span class="p">(</span><span class="kt">int</span> <span class="n">j</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span> <span class="n">j</span><span class="o">&lt;</span><span class="n">b</span><span class="p">;</span> <span class="n">j</span><span class="o">++</span><span class="p">){</span> <span class="k">if</span><span class="p">(</span><span class="n">D</span><span class="p">[</span><span class="n">i</span><span class="o">-</span><span class="mi">1</span><span class="o">&amp;</span><span class="mi">1</span><span class="p">][</span><span class="n">a</span><span class="p">][</span><span class="n">j</span><span class="p">][</span><span class="n">c</span><span class="p">][</span><span class="mi">0</span><span class="p">]</span> <span class="o">!=</span> <span class="o">-</span><span class="mi">1</span><span class="p">)</span> <span class="n">now</span> <span class="o">=</span> <span class="n">max</span><span class="p">(</span><span class="n">now</span><span class="p">,</span> <span class="n">D</span><span class="p">[</span><span class="n">i</span><span class="o">-</span><span class="mi">1</span><span class="o">&amp;</span><span class="mi">1</span><span class="p">][</span><span class="n">a</span><span class="p">][</span><span class="n">j</span><span class="p">][</span><span class="n">c</span><span class="p">][</span><span class="mi">0</span><span class="p">]</span> <span class="o">+</span> <span class="n">B</span><span class="p">[</span><span class="n">b</span><span class="p">].</span><span class="n">second</span><span class="p">);</span> <span class="k">if</span><span class="p">(</span><span class="n">D</span><span class="p">[</span><span class="n">i</span><span class="o">-</span><span class="mi">1</span><span class="o">&amp;</span><span class="mi">1</span><span class="p">][</span><span class="n">a</span><span class="p">][</span><span class="n">j</span><span class="p">][</span><span class="n">c</span><span class="p">][</span><span class="mi">2</span><span class="p">]</span> <span class="o">!=</span> <span class="o">-</span><span class="mi">1</span><span class="p">)</span> <span class="n">now</span> <span class="o">=</span> <span class="n">max</span><span class="p">(</span><span class="n">now</span><span class="p">,</span> <span class="n">D</span><span class="p">[</span><span class="n">i</span><span class="o">-</span><span class="mi">1</span><span class="o">&amp;</span><span class="mi">1</span><span class="p">][</span><span class="n">a</span><span class="p">][</span><span class="n">j</span><span class="p">][</span><span class="n">c</span><span class="p">][</span><span class="mi">2</span><span class="p">]</span> <span class="o">+</span> <span class="n">B</span><span class="p">[</span><span class="n">b</span><span class="p">].</span><span class="n">second</span><span class="p">);</span> <span class="p">}</span> <span class="p">}</span> <span class="k">if</span><span class="p">(</span><span class="n">C</span><span class="p">[</span><span class="n">c</span><span class="p">].</span><span class="n">first</span> <span class="o">&lt;=</span> <span class="n">L</span><span class="p">[</span><span class="n">i</span><span class="p">]){</span> <span class="kt">int</span> <span class="o">&amp;</span><span class="n">now</span> <span class="o">=</span> <span class="n">D</span><span class="p">[</span><span class="n">i</span><span class="o">&amp;</span><span class="mi">1</span><span class="p">][</span><span class="n">a</span><span class="p">][</span><span class="n">b</span><span class="p">][</span><span class="n">c</span><span class="p">][</span><span class="mi">2</span><span class="p">];</span> <span class="k">for</span><span class="p">(</span><span class="kt">int</span> <span class="n">j</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span> <span class="n">j</span><span class="o">&lt;</span><span class="n">c</span><span class="p">;</span> <span class="n">j</span><span class="o">++</span><span class="p">){</span> <span class="k">if</span><span class="p">(</span><span class="n">D</span><span class="p">[</span><span class="n">i</span><span class="o">-</span><span class="mi">1</span><span class="o">&amp;</span><span class="mi">1</span><span class="p">][</span><span class="n">a</span><span class="p">][</span><span class="n">b</span><span class="p">][</span><span class="n">j</span><span class="p">][</span><span class="mi">0</span><span class="p">]</span> <span class="o">!=</span> <span class="o">-</span><span class="mi">1</span><span class="p">)</span> <span class="n">now</span> <span class="o">=</span> <span class="n">max</span><span class="p">(</span><span class="n">now</span><span class="p">,</span> <span class="n">D</span><span class="p">[</span><span class="n">i</span><span class="o">-</span><span class="mi">1</span><span class="o">&amp;</span><span class="mi">1</span><span class="p">][</span><span class="n">a</span><span class="p">][</span><span class="n">b</span><span class="p">][</span><span class="n">j</span><span class="p">][</span><span class="mi">0</span><span class="p">]</span> <span class="o">+</span> <span class="n">C</span><span class="p">[</span><span class="n">c</span><span class="p">].</span><span class="n">second</span><span class="p">);</span> <span class="k">if</span><span class="p">(</span><span class="n">D</span><span class="p">[</span><span class="n">i</span><span class="o">-</span><span class="mi">1</span><span class="o">&amp;</span><span class="mi">1</span><span class="p">][</span><span class="n">a</span><span class="p">][</span><span class="n">b</span><span class="p">][</span><span class="n">j</span><span class="p">][</span><span class="mi">1</span><span class="p">]</span> <span class="o">!=</span> <span class="o">-</span><span class="mi">1</span><span class="p">)</span> <span class="n">now</span> <span class="o">=</span> <span class="n">max</span><span class="p">(</span><span class="n">now</span><span class="p">,</span> <span class="n">D</span><span class="p">[</span><span class="n">i</span><span class="o">-</span><span class="mi">1</span><span class="o">&amp;</span><span class="mi">1</span><span class="p">][</span><span class="n">a</span><span class="p">][</span><span class="n">b</span><span class="p">][</span><span class="n">j</span><span class="p">][</span><span class="mi">1</span><span class="p">]</span> <span class="o">+</span> <span class="n">C</span><span class="p">[</span><span class="n">c</span><span class="p">].</span><span class="n">second</span><span class="p">);</span> <span class="p">}</span> <span class="p">}</span> <span class="p">}</span> <span class="kt">int</span> <span class="nf">main</span><span class="p">(){</span> <span class="n">ios_base</span><span class="o">::</span><span class="n">sync_with_stdio</span><span class="p">(</span><span class="nb">false</span><span class="p">);</span> <span class="n">cin</span><span class="p">.</span><span class="n">tie</span><span class="p">(</span><span class="nb">nullptr</span><span class="p">);</span> <span class="n">cin</span> <span class="o">&gt;&gt;</span> <span class="n">N</span><span class="p">;</span> <span class="k">for</span><span class="p">(</span><span class="kt">int</span> <span class="n">i</span><span class="o">=</span><span class="mi">1</span><span class="p">;</span> <span class="n">i</span><span class="o">&lt;=</span><span class="n">N</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="n">cin</span> <span class="o">&gt;&gt;</span> <span class="n">A</span><span class="p">[</span><span class="n">i</span><span class="p">].</span><span class="n">second</span> <span class="o">&gt;&gt;</span> <span class="n">A</span><span class="p">[</span><span class="n">i</span><span class="p">].</span><span class="n">first</span><span class="p">;</span> <span class="k">for</span><span class="p">(</span><span class="kt">int</span> <span class="n">i</span><span class="o">=</span><span class="mi">1</span><span class="p">;</span> <span class="n">i</span><span class="o">&lt;=</span><span class="n">N</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="n">cin</span> <span class="o">&gt;&gt;</span> <span class="n">B</span><span class="p">[</span><span class="n">i</span><span class="p">].</span><span class="n">second</span> <span class="o">&gt;&gt;</span> <span class="n">B</span><span class="p">[</span><span class="n">i</span><span class="p">].</span><span class="n">first</span><span class="p">;</span> <span class="k">for</span><span class="p">(</span><span class="kt">int</span> <span class="n">i</span><span class="o">=</span><span class="mi">1</span><span class="p">;</span> <span class="n">i</span><span class="o">&lt;=</span><span class="n">N</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="n">cin</span> <span class="o">&gt;&gt;</span> <span class="n">C</span><span class="p">[</span><span class="n">i</span><span class="p">].</span><span class="n">second</span> <span class="o">&gt;&gt;</span> <span class="n">C</span><span class="p">[</span><span class="n">i</span><span class="p">].</span><span class="n">first</span><span class="p">;</span> <span class="k">for</span><span class="p">(</span><span class="kt">int</span> <span class="n">i</span><span class="o">=</span><span class="mi">1</span><span class="p">;</span> <span class="n">i</span><span class="o">&lt;=</span><span class="n">N</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="n">cin</span> <span class="o">&gt;&gt;</span> <span class="n">L</span><span class="p">[</span><span class="n">i</span><span class="p">];</span> <span class="n">sort</span><span class="p">(</span><span class="n">A</span><span class="o">+</span><span class="mi">1</span><span class="p">,</span> <span class="n">A</span><span class="o">+</span><span class="n">N</span><span class="o">+</span><span class="mi">1</span><span class="p">);</span> <span class="n">sort</span><span class="p">(</span><span class="n">B</span><span class="o">+</span><span class="mi">1</span><span class="p">,</span> <span class="n">B</span><span class="o">+</span><span class="n">N</span><span class="o">+</span><span class="mi">1</span><span class="p">);</span> <span class="n">sort</span><span class="p">(</span><span class="n">C</span><span class="o">+</span><span class="mi">1</span><span class="p">,</span> <span class="n">C</span><span class="o">+</span><span class="n">N</span><span class="o">+</span><span class="mi">1</span><span class="p">);</span> <span class="n">memset</span><span class="p">(</span><span class="n">D</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="k">sizeof</span> <span class="n">D</span><span class="p">);</span> <span class="k">for</span><span class="p">(</span><span class="kt">int</span> <span class="n">i</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span> <span class="n">i</span><span class="o">&lt;</span><span class="mi">3</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="n">D</span><span class="p">[</span><span class="mi">0</span><span class="p">][</span><span class="mi">0</span><span class="p">][</span><span class="mi">0</span><span class="p">][</span><span class="mi">0</span><span class="p">][</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="k">for</span><span class="p">(</span><span class="kt">int</span> <span class="n">i</span><span class="o">=</span><span class="mi">1</span><span class="p">;</span> <span class="n">i</span><span class="o">&lt;=</span><span class="n">N</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">){</span> <span class="n">memset</span><span class="p">(</span><span class="n">D</span><span class="p">[</span><span class="n">i</span><span class="o">&amp;</span><span class="mi">1</span><span class="p">],</span> <span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">D</span><span class="p">)</span> <span class="o">/</span> <span class="mi">2</span><span class="p">);</span> <span class="k">for</span><span class="p">(</span><span class="kt">int</span> <span class="n">a</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span> <span class="n">a</span><span class="o">&lt;=</span><span class="n">N</span><span class="p">;</span> <span class="n">a</span><span class="o">++</span><span class="p">)</span> <span class="k">for</span><span class="p">(</span><span class="kt">int</span> <span class="n">b</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span> <span class="n">b</span><span class="o">&lt;=</span><span class="n">N</span><span class="p">;</span> <span class="n">b</span><span class="o">++</span><span class="p">)</span> <span class="k">for</span><span class="p">(</span><span class="kt">int</span> <span class="n">c</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span> <span class="n">c</span><span class="o">&lt;=</span><span class="n">N</span><span class="p">;</span> <span class="n">c</span><span class="o">++</span><span class="p">)</span> <span class="n">GetDP</span><span class="p">(</span><span class="n">i</span><span class="p">,</span> <span class="n">a</span><span class="p">,</span> <span class="n">b</span><span class="p">,</span> <span class="n">c</span><span class="p">);</span> <span class="p">}</span> <span class="kt">int</span> <span class="n">mx</span> <span class="o">=</span> <span class="o">-</span><span class="mi">1</span><span class="p">;</span> <span class="k">for</span><span class="p">(</span><span class="kt">int</span> <span class="n">a</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span> <span class="n">a</span><span class="o">&lt;=</span><span class="n">N</span><span class="p">;</span> <span class="n">a</span><span class="o">++</span><span class="p">)</span> <span class="k">for</span><span class="p">(</span><span class="kt">int</span> <span class="n">b</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span> <span class="n">b</span><span class="o">&lt;=</span><span class="n">N</span><span class="p">;</span> <span class="n">b</span><span class="o">++</span><span class="p">)</span> <span class="k">for</span><span class="p">(</span><span class="kt">int</span> <span class="n">c</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span> <span class="n">c</span><span class="o">&lt;=</span><span class="n">N</span><span class="p">;</span> <span class="n">c</span><span class="o">++</span><span class="p">){</span> <span class="k">for</span><span class="p">(</span><span class="kt">int</span> <span class="n">d</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span> <span class="n">d</span><span class="o">&lt;</span><span class="mi">3</span><span class="p">;</span> <span class="n">d</span><span class="o">++</span><span class="p">)</span> <span class="n">mx</span> <span class="o">=</span> <span class="n">max</span><span class="p">(</span><span class="n">mx</span><span class="p">,</span> <span class="n">D</span><span class="p">[</span><span class="n">N</span><span class="o">&amp;</span><span class="mi">1</span><span class="p">][</span><span class="n">a</span><span class="p">][</span><span class="n">b</span><span class="p">][</span><span class="n">c</span><span class="p">][</span><span class="n">d</span><span class="p">]);</span> <span class="p">}</span> <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">mx</span><span class="p">;</span> <span class="p">}</span> </code></pre></div></div> <h4 id="분기-예측">분기 예측</h4> <p><code class="language-plaintext highlighter-rouge">GetDP</code> 함수의 각 for문마다 복잡한 if문이 2개씩 있습니다. <code class="language-plaintext highlighter-rouge">-1</code>과 비교하고, 다시 <code class="language-plaintext highlighter-rouge">now</code>와 비교해서 더 큰 값을 <code class="language-plaintext highlighter-rouge">now</code>에 대입하는 것은 매우 복잡합니다.<br /> 현대 CPU는 <strong>분기 예측</strong>이라는 기술을 사용합니다. 이는 조건문이 어떤 곳으로 분기할 것인지 미리 예측해서 계산하는 기술입니다. 하지만 예측에 실패하면 미리 계산한 결과를 폐기해야 하기 때문에 어느정도 손해를 보게 됩니다. BOJ 채점 서버에서 사용하는 Intel Haswell의 경우 분기 예측 실패시 15-20 클럭 정도를 낭비한다고 합니다.<br /> PS는 최악의 경우를 고려해야 하기 때문에 분기 예측 실패가 발생할 여지를 줄여야 합니다. 조건문의 개수를 줄여봅시다.</p> <p>아래 코드는 위에서 본 복잡한 조건문 대신 <code class="language-plaintext highlighter-rouge">max</code> 함수만 2개씩 사용합니다. 이 코드의 실행 시간은 <strong>900ms</strong> 이며, 초기 버전에 비해 1500ms 정도 감소했습니다.</p> <div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">void</span> <span class="nf">GetDP</span><span class="p">(</span><span class="kt">int</span> <span class="n">i</span><span class="p">,</span> <span class="kt">int</span> <span class="n">a</span><span class="p">,</span> <span class="kt">int</span> <span class="n">b</span><span class="p">,</span> <span class="kt">int</span> <span class="n">c</span><span class="p">){</span> <span class="k">if</span><span class="p">(</span><span class="n">A</span><span class="p">[</span><span class="n">a</span><span class="p">].</span><span class="n">first</span> <span class="o">&lt;=</span> <span class="n">L</span><span class="p">[</span><span class="n">i</span><span class="p">]){</span> <span class="kt">int</span> <span class="n">now</span> <span class="o">=</span> <span class="o">-</span><span class="mi">1</span><span class="p">;</span> <span class="k">for</span><span class="p">(</span><span class="kt">int</span> <span class="n">j</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span> <span class="n">j</span><span class="o">&lt;</span><span class="n">a</span><span class="p">;</span> <span class="n">j</span><span class="o">++</span><span class="p">){</span> <span class="n">now</span> <span class="o">=</span> <span class="n">max</span><span class="p">(</span><span class="n">now</span><span class="p">,</span> <span class="n">D</span><span class="p">[</span><span class="n">i</span><span class="o">-</span><span class="mi">1</span><span class="o">&amp;</span><span class="mi">1</span><span class="p">][</span><span class="n">j</span><span class="p">][</span><span class="n">b</span><span class="p">][</span><span class="n">c</span><span class="p">][</span><span class="mi">1</span><span class="p">]);</span> <span class="n">now</span> <span class="o">=</span> <span class="n">max</span><span class="p">(</span><span class="n">now</span><span class="p">,</span> <span class="n">D</span><span class="p">[</span><span class="n">i</span><span class="o">-</span><span class="mi">1</span><span class="o">&amp;</span><span class="mi">1</span><span class="p">][</span><span class="n">j</span><span class="p">][</span><span class="n">b</span><span class="p">][</span><span class="n">c</span><span class="p">][</span><span class="mi">2</span><span class="p">]);</span> <span class="p">}</span> <span class="k">if</span><span class="p">(</span><span class="n">now</span> <span class="o">!=</span> <span class="o">-</span><span class="mi">1</span><span class="p">)</span> <span class="n">D</span><span class="p">[</span><span class="n">i</span><span class="o">&amp;</span><span class="mi">1</span><span class="p">][</span><span class="n">a</span><span class="p">][</span><span class="n">b</span><span class="p">][</span><span class="n">c</span><span class="p">][</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="n">now</span> <span class="o">+</span> <span class="n">A</span><span class="p">[</span><span class="n">a</span><span class="p">].</span><span class="n">second</span><span class="p">;</span> <span class="p">}</span> <span class="k">if</span><span class="p">(</span><span class="n">B</span><span class="p">[</span><span class="n">b</span><span class="p">].</span><span class="n">first</span> <span class="o">&lt;=</span> <span class="n">L</span><span class="p">[</span><span class="n">i</span><span class="p">]){</span> <span class="kt">int</span> <span class="n">now</span> <span class="o">=</span> <span class="o">-</span><span class="mi">1</span><span class="p">;</span> <span class="k">for</span><span class="p">(</span><span class="kt">int</span> <span class="n">j</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span> <span class="n">j</span><span class="o">&lt;</span><span class="n">b</span><span class="p">;</span> <span class="n">j</span><span class="o">++</span><span class="p">){</span> <span class="n">now</span> <span class="o">=</span> <span class="n">max</span><span class="p">(</span><span class="n">now</span><span class="p">,</span> <span class="n">D</span><span class="p">[</span><span class="n">i</span><span class="o">-</span><span class="mi">1</span><span class="o">&amp;</span><span class="mi">1</span><span class="p">][</span><span class="n">a</span><span class="p">][</span><span class="n">j</span><span class="p">][</span><span class="n">c</span><span class="p">][</span><span class="mi">0</span><span class="p">]);</span> <span class="n">now</span> <span class="o">=</span> <span class="n">max</span><span class="p">(</span><span class="n">now</span><span class="p">,</span> <span class="n">D</span><span class="p">[</span><span class="n">i</span><span class="o">-</span><span class="mi">1</span><span class="o">&amp;</span><span class="mi">1</span><span class="p">][</span><span class="n">a</span><span class="p">][</span><span class="n">j</span><span class="p">][</span><span class="n">c</span><span class="p">][</span><span class="mi">2</span><span class="p">]);</span> <span class="p">}</span> <span class="k">if</span><span class="p">(</span><span class="n">now</span> <span class="o">!=</span> <span class="o">-</span><span class="mi">1</span><span class="p">)</span> <span class="n">D</span><span class="p">[</span><span class="n">i</span><span class="o">&amp;</span><span class="mi">1</span><span class="p">][</span><span class="n">a</span><span class="p">][</span><span class="n">b</span><span class="p">][</span><span class="n">c</span><span class="p">][</span><span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="n">now</span> <span class="o">+</span> <span class="n">B</span><span class="p">[</span><span class="n">b</span><span class="p">].</span><span class="n">second</span><span class="p">;</span> <span class="p">}</span> <span class="k">if</span><span class="p">(</span><span class="n">C</span><span class="p">[</span><span class="n">c</span><span class="p">].</span><span class="n">first</span> <span class="o">&lt;=</span> <span class="n">L</span><span class="p">[</span><span class="n">i</span><span class="p">]){</span> <span class="kt">int</span> <span class="n">now</span> <span class="o">=</span> <span class="o">-</span><span class="mi">1</span><span class="p">;</span> <span class="k">for</span><span class="p">(</span><span class="kt">int</span> <span class="n">j</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span> <span class="n">j</span><span class="o">&lt;</span><span class="n">c</span><span class="p">;</span> <span class="n">j</span><span class="o">++</span><span class="p">){</span> <span class="n">now</span> <span class="o">=</span> <span class="n">max</span><span class="p">(</span><span class="n">now</span><span class="p">,</span> <span class="n">D</span><span class="p">[</span><span class="n">i</span><span class="o">-</span><span class="mi">1</span><span class="o">&amp;</span><span class="mi">1</span><span class="p">][</span><span class="n">a</span><span class="p">][</span><span class="n">b</span><span class="p">][</span><span class="n">j</span><span class="p">][</span><span class="mi">0</span><span class="p">]);</span> <span class="n">now</span> <span class="o">=</span> <span class="n">max</span><span class="p">(</span><span class="n">now</span><span class="p">,</span> <span class="n">D</span><span class="p">[</span><span class="n">i</span><span class="o">-</span><span class="mi">1</span><span class="o">&amp;</span><span class="mi">1</span><span class="p">][</span><span class="n">a</span><span class="p">][</span><span class="n">b</span><span class="p">][</span><span class="n">j</span><span class="p">][</span><span class="mi">1</span><span class="p">]);</span> <span class="p">}</span> <span class="k">if</span><span class="p">(</span><span class="n">now</span> <span class="o">!=</span> <span class="o">-</span><span class="mi">1</span><span class="p">)</span> <span class="n">D</span><span class="p">[</span><span class="n">i</span><span class="o">&amp;</span><span class="mi">1</span><span class="p">][</span><span class="n">a</span><span class="p">][</span><span class="n">b</span><span class="p">][</span><span class="n">c</span><span class="p">][</span><span class="mi">2</span><span class="p">]</span> <span class="o">=</span> <span class="n">now</span> <span class="o">+</span> <span class="n">C</span><span class="p">[</span><span class="n">c</span><span class="p">].</span><span class="n">second</span><span class="p">;</span> <span class="p">}</span> <span class="p">}</span> </code></pre></div></div> <h4 id="컴파일러-최적화">컴파일러 최적화</h4> <p>혹시 모르니 O3 최적화를 켜고 AVX2 명령어를 사용할 수 있도록 컴파일 옵션을 넣어봅시다.<br /> <code class="language-plaintext highlighter-rouge">-O3</code> 옵션만 넣은 코드의 실행 시간은 여전히 <strong>900ms</strong> 정도지만, <code class="language-plaintext highlighter-rouge">-mavx2</code> 옵션까지 넣은 코드의 실행 시간은 <strong>600ms</strong> 정도입니다. 처음에 작성한 코드에 비해 4배 가까이 빨라졌습니다.</p> <div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#pragma GCC optimize ("O3") #pragma GCC target ("avx2") #include &lt;bits/stdc++.h&gt; </span><span class="k">using</span> <span class="k">namespace</span> <span class="n">std</span><span class="p">;</span> <span class="c1">// 이하 생략</span> </code></pre></div></div>JusticeHui서론 2022년 성균관대학교 프로그래밍 경진대회에 검수자로 참여했습니다. 이번 대회에는 저보다 훨씬 실력이 좋은 분들이 검수자로 함께 참여했습니다. 다른 검수자들이 저보다 훨씬 빠르고 정확하게 풀이 검증을 해주셨기 때문에 저는 상수 최적화를 통한 데이터 강화에 더 집중했습니다. 평소에는 대회 검수 후기를 잘 남기지 않는 편이지만, 이번 검수에서 시도한 상수 최적화 방법들이 인상적이라서 오랜만에 검수 후기를 작성해봅니다. 캐시 히트, 분기 예측, SIMD를 알고 있으면 글을 읽는데 도움이 될 수 있습니다.2022.02.01 설날맞이 PS 일지2022-02-01T14:59:00+00:002022-02-01T14:59:00+00:00https://justicehui.github.io/ps/2022/02/01/lunar-new-year<h3 id="서론">서론</h3> <p>2022년 설날을 기념해 BOJ에서 민속놀이 관련 문제들을 풀어보았습니다.</p> <h3 id="문제-목록">문제 목록</h3> <p><a href="https://www.acmicpc.net/workbook/view/10155">BOJ 문제집</a></p> <ul> <li>줄다리기 <ul> <li>BOJ 2488 줄다리기</li> </ul> </li> <li>윷놀이 <ul> <li>BOJ 2490 윷놀이</li> <li>BOJ 17825 주사위 윷놀이</li> <li>BOJ 12425 윷놀이 (Small)</li> <li>BOJ 12426 윷놀이 (Large)</li> <li>BOJ 15778 Yut Nori</li> </ul> </li> <li>장기 <ul> <li>BOJ 16509 장군</li> <li>BOJ 1846 장기</li> <li>BOJ 23041 뛰는 기물</li> </ul> </li> <li>스타크래프트 <ul> <li>BOJ 1002 터렛</li> <li>BOJ 12869 뮤탈리스크</li> <li>BOJ 12870 뮤탈리스크 2</li> <li>BOJ 1031 스타 대결</li> <li>BOJ 1608 스타 대회</li> </ul> </li> <li>BOJ 18890 Seollal</li> </ul> <h3 id="2488-줄다리기">2488 줄다리기</h3> <p>배열의 앞과 뒤에서 각각 조건을 만족하는 구간을 찾은 다음, 가운데 구간이 조건을 만족하는지 확인하면 될 것 같다는 생각을 할 수 있습니다. 조건을 만족하는 Prefix를 찾는 방법을 알아봅시다.</p> <p>편의상 $X_i = \sum_{k=1}^{i} A_k,\ Y_i = \sum_{k=1}^{i} B_k$라고 정의합시다. 각 $i$마다 $\vert X_i - Y_j \vert \leq 50$을 만족하는 $j$를 찾을 것입니다. 즉, $X_i-50 \leq Y_j \leq X_i+50$을 만족하는 $j$를 찾아야 합니다.<br />이때 $X_i - 50 \leq Y_j$를 만족하는 $j$는 $i$가 증가함에 따라 단조증가하고, $\vert X_i-Y_j\vert \leq 50$을 만족하는 $j$는 구간을 이룹니다. 그러므로 투포인터를 이용해 구간의 시작점을 구한 뒤, 조건을 만족하는 구간의 끝점까지 모두 훑어주면 됩니다. 배열의 원소는 20 이상이므로 $i$마다 조건을 만족하는 $j$는 최대 6개 존재하고, 모든 Prefix를 $O(N+M)$ 시간에 구할 수 있습니다. Suffix는 인덱스를 반대로 보면 되고, 마찬가지로 $O(N+M)$에 구할 수 있습니다.</p> <p>$-50\leq k \leq 50$인 $K$에 대해 $X_i - Y_j = k$을 만족하는 가장 앞에 있는 Prefix의 마지막 인덱스를 각각 $Pi_k, Pj_k$, 가장 뒤에 있는 Suffix의 첫 인덱스를 각각 $Si_k, Sj_k$라고 합시다. 모든 $-50 \leq u,v \leq 50$를 보면서, 중간 구간인 $A[Pi_u+1\cdots Si_v-1]$과 $B[Pj_u+1\cdots Sj_v-1]$이 조건을 만족하는지 확인하면 문제를 해결할 수 있습니다.</p> <p>시간 복잡도는 Prefix와 Suffix를 구하는데 $O(N+M)$, 중간 구간을 확인하는데 $O(100^2)$이 걸리므로 $O(N+M)$입니다.</p> <h3 id="2490-윷놀이">2490 윷놀이</h3> <p>단순 구현 (<a href="https://www.acmicpc.net/source/share/7a51901d046b4fa48d44e06ebd2a6884">코드</a>)</p> <h3 id="17825-주사위-윷놀이">17825 주사위 윷놀이</h3> <p>완전 탐색 (<a href="http://boj.kr/07d90e254b88497fb0757e4d39c96681">코드</a>)</p> <h3 id="1242512426-윷놀이">12425/12426 윷놀이</h3> <p>$D(i, player, a_1, a_2, b_1, b_2) := $ $i$번째 윷을 $player$가 던진 직후에 첫 번째 플레이어의 말이 $a_1, a_2$, 두 번째 플레이어의 말이 $b_1, b_2$에 있을 수 있으면 1, 없으면 0으로 DP 돌리면 될텐데 짜기 싫어서 안 짰습니다.</p> <h3 id="15778-yut-nori">15778 Yut Nori</h3> <p>구현 노가다 (<a href="http://boj.kr/59705ed18ecf45b89f7e46f00be2ba1f">코드</a>)</p> <h3 id="16509-장군">16509 장군</h3> <p>BFS 연습 문제 (<a href="http://boj.kr/b7bff72ea02c4c948228354aa137a1d3">코드</a>)</p> <h3 id="1846-장기">1846 장기</h3> <p>$i &lt; N$일 때 $(i,i+1)$, 마지막 행에는 $(N,1)$에 차를 배치한 다음, 대각선 위에 있는 것들만 적절히 옮기는 방식으로 문제를 해결할 수 있습니다.</p> <p><strong>(Method 1)</strong> $(N, 1)$은 대각선에 포함되므로 $N-2$번째 행과 교환합시다.<br />현재 상황은 $i \leq N-3$일 때 $(i, i+1)$, 그리고 $(N-2,1)$, $(N-1,N)$, $(N,N-1)$에 차가 배치되어 있습니다. $N=5$ 상황을 손으로 그려보면 올바른 배치임을 확인할 수 있습니다.</p> <p>$(i, j)$가 대각선 위에 있는 것은 $i = j \lor i+j=N+1$과 동치입니다. Method 1에서 $i=j$가 성립하는 경우는 $N = 3$일 때의 $(N-2, 1)$ 밖에 없습니다. $N=3$일 때는 정답이 존재하지 않으므로 예외 처리를 합시다.<br />Method 1에서 $i+j=N+1$이 성립하는 경우는 $i=N/2$일 때 $(i, i+1)$ 밖에 없습니다. 이건 어떻게 처리해야 할까요?</p> <p><strong>(Method 2)</strong> $N$이 짝수일 때 $(N/2, N/2+1)$은 대각선에 포함되므로 $1$번째 행과 교환합시다.<br />현재 상황은 $2\leq i \leq N-3, i\neq N/2$일 때 $(i, i+1)$, 그리고 $(1, N/2+1)$, $(N/2, 2)$, $(N-2, 1)$, $(N-1, N)$, $(N, N-1)$에 차가 배치되어 있습니다.</p> <p>Method 2는 $4$를 제외한 모든 짝수 $N$에서 올바른 답을 출력합니다. $N = 4$일 때만 손으로 직접 만들면 문제를 해결할 수 있습니다.</p> <h3 id="23041-뛰는-기물">23041 뛰는 기물</h3> <p>정수론 냄새가 납니다.<br />격자를 $\gcd(N,M)$칸씩 분할하면, 기물은 같은 영역 안에 있는 다른 칸에 도달하지 못합니다. 그러므로 $N/\gcd(N,M),\ M/\gcd(N,M)$인 문제를 풀고 $gcd(N,M)^2$을 정답에 곱하면 됩니다.</p> <p>이제 $N, M$이 서로소인 문제를 풀어봅시다. 일반성을 잃지 않고, $N \leq M$이라고 합시다.<br />만약 $N = M = 1$이면 체스의 비숍처럼 이동하기 때문에 $N+M$의 기우성이 항상 유지되어서 정답은 2입니다. $N = 0$이면 $M = 1$이고, 정답은 1입니다. 이제 $1 \leq N &lt; M$이라고 가정해도 됩니다.</p> <p>여기에서 바로 규칙을 찾는 것은 어려워 보입니다. 유클리드 호제법처럼 문제를 더 작은 수로 <strong>“축소”</strong>할 수 있는지 생각해봅시다.</p> <p>$1 \leq N &lt; M$은 $2N &lt; M$, $2N = M$, $M &lt; 2N$ 총 3가지 경우로 나눌 수 있습니다.<br />$1 \leq N &lt; 2N &lt; M$이면 $(0, 0) \rightarrow (N,M) \rightarrow (N+M, M-N) \rightarrow (N, M-2N)$으로 최댓값을 감소시킬 수 있습니다.<br />$1 \leq N &lt; M &lt; 2N$이면 $(0,0) \rightarrow (M,N) \rightarrow (N+M, N-M) \rightarrow (N, 2N-M)$으로 최댓값을 감소시킬 수 있습니다.</p> <p>$(N,M)$을 $(N,M-2N)$ 또는 $(N,2N-M)$으로 바꾸는 연산을 해도 최대 공약수는 바뀌지 않습니다. 그러므로 $1 \leq N,\ N \neq M,\ M \neq 2N$이면 항상 위 두 연산을 적용해 최댓값을 줄여나갈 수 있습니다.<br />만약 $N = 0$이 되어서 끝난다면 $N = 0,\ M = 1$이므로 정답은 1입니다.<br />$N=M$이 되어서 끝난다면 $N=M=1$이므로 정답은 2입니다.<br />$M=2N$이 되어서 끝난다면 $N=1, M=2$, 즉 馬가 되므로 정답은 1입니다.</p> <p>한 가지 더 관찰해야 하는 사실은, $(N,M)$을 $(N,M-2N)$ 또는 $(N,2N-M)$으로 바꾸는 연산을 해도 두 수의 합의 기우성이 유지된다는 점입니다.<br />$N=0,\ M=2N$으로 끝나는 경우에는 $N+M$이 홀수이고, 정답은 1입니다.<br />$N=M$으로 끝나는 경우에는 $N+M$이 짝수이고, 정답은 2입니다.</p> <h3 id="1002-터렛">1002 터렛</h3> <p>원의 방정식 (<a href="http://boj.kr/20f3fd15ab274016b16512bafe12e6a1">코드</a>)</p> <h3 id="1286912870-뮤탈리스크">12869/12870 뮤탈리스크</h3> <p>12869는 SCV가 최대 3개 있으므로 “$D[h_1][h_2][h_3] := i$번째 SCV의 남은 체력이 $h_i$일 때 필요한 공격 횟수의 최솟값”으로 DP를 하면 $O(60^N)$에 문제를 해결할 수 있습니다. 상태가 $O(60^N)$가지 존재하고, 각 상태마다 $3!$가지의 상태 전이를 계산해야 하기 때문입니다.</p> <p>12870은 SCV가 최대 20개 있으므로 더 효율적으로 해결해야 합니다. 답을 $L$이라고 고정해서 파라메트릭 서치를 해봅시다. $i$번째 SCV의 체력을 $H_i$, 대미지가 9, 3, 1인 공격 횟수를 각각 $A_i, B_i, C_i$라고 하면, 우리는 아래 조건을 만족하는 가장 작은 $L$을 찾아야 합니다.</p> <ul> <li>$9A_i + 3B_i + C_i \geq H_i$</li> <li>$\sum A_i \leq L,\ \sum B_i \leq L,\ \sum C_i \leq L$</li> <li>$\forall i,\ A_i+B_i+C_i \leq L$</li> </ul> <p>그러므로 $D(i,j,k)$를 “대미지가 9인 공격과 3인 공격을 각각 $j, k$번 했을 때, $1\cdots i$번째 SCV를 모두 죽이기 위해 필요한 대미지 1짜리 공격 횟수의 최솟값”으로 정의해서 DP를 할 수 있습니다. $L$은 항상 $7N$ 미만이므로 상태는 최대 $O(N^3)$개 존재합니다.</p> <p>상태 전이는 $D(i,j,k) \leftarrow \min\lbrace D(i,j,k),\ D(i-1,j-a,k-b)+c\rbrace$ 와 같이 할 수 있습니다. 조건을 만족하는 $0 \leq a \leq j \leq L, 0 \leq b \leq k \leq L$에 대해 모두 계산해야 하므로, 각 상태마다 $O(L^2) = O(N^2)$가지 상태 전이를 계산해야 합니다.</p> <p>전체 시간 복잡도는 상수가 큰 $O(N^5)$이고, 반복문 범위를 적당히 잘라주면 문제를 해결할 수 있습니다.</p> <h3 id="1031-스타-대결">1031 스타 대결</h3> <p>가능한 대진표를 아무거나 출력하는 것은 간단한 최대 유량 문제입니다. 하지만 사전순 최소 조건이 있기 때문에 간단하지 않은 문제가 되었습니다.<br />$O(N^6)$ 풀이와 $O(N^4)$풀이가 있고, 두 풀이 모두 통과됩니다. 차례대로 소개합니다.</p> <p>$O(N^6)$ 풀이는 사전순 최소를 구할 때 자주 사용하는 테크닉입니다. 앞에서부터 차례대로 보면서, 0으로 만들 수 있으면 0을 출력하고, 0으로 만들 수 없으면 1을 출력하는 방식으로 진행합니다. $(i, j)$의 결과를 결정할 때, $(i, j)$보다 사전순으로 큰 간선들만 사용해서 최대 유량을 동일하게 만들 수 있다면 0을 출력하고, 만들 수 없다면 1을 출력하면 됩니다.<br />Dinic’s Algorithm을 사용한다고 가정하면, 최대 유량을 $O(N^2)$번 구해야 하므로 $O(N^6)$입니다. 제 코드는 368ms로 넉넉하게 통과되던데, 난이도 의견을 보면 시간 초과를 받으신 분들도 계신 것 같습니다. 최적화를 신경써서 코딩을 해야 합니다.</p> <p>$O(N^4)$ 풀이는 네트워크 플로우 문제에서 흔히 보이는 테크닉을 사용합니다. 일단 가능한 대진표를 아무거나 만듭니다. 그 다음에 만약 $(i, j)$가 1이라면, $S\rightarrow i \rightarrow j \rightarrow T$ 경로에 흘린 유량을 취소시킨 뒤, $(i, j)$보다 사전순으로 큰 간선을 이용하는 증가 경로가 있는지 탐색합니다. 만약 그러한 증가 경로가 있다면 그 경로로 유량을 흘려야 사전순으로 더 작은 결과물을 얻을 수 있습니다.<br />증가 경로를 찾는 것은 BFS를 이용해 $O(N^2)$에 할 수 있고, 최대 $O(N^2)$번 하므로 전체 시간 복잡도는 $O(N^4)$입니다.</p> <h3 id="1608-스타-대회">1608 스타 대회</h3> <p>만약 $a$에서 $b$까지 가는 경로가 존재한다면 $a$는 경로 위에 있는 다른 모든 정점을 이길 수 있습니다. 그러므로 같은 SCC에 속한 정점들을 동등하게 생각할 수 있습니다. 이제 DAG에서 문제를 해결해봅시다.</p> <p>만약 $a$에서 $b$로 가는 <strong>경로</strong>가 존재하면서 $b$가 우승할 수 있다면 $a$도 우승할 수 있습니다. 그러므로 in-degree가 0인 정점은 항상 우승할 수 있습니다.<br />만약 $a$에서 $b$로 가는 <strong>간선</strong>이 존재하지 않으면서 $a$가 우승할 수 있다면 $b$도 우승할 수 있습니다. 그러므로 어떤 정점 $v$보다 위상 정렬 상으로 앞선 정점들 중, $v$로 가는 간선이 존재하지 않는 정점이 우승할 수 있다면 $v$도 우승할 수 있습니다.</p> <p>이 성질을 이용해 정점을 위상 정렬 순서대로 보면서, 현재 단계 이전까지 우승할 수 있는 정점의 개수를 관리해주면 문제를 해결할 수 있습니다.<br />만약 간선의 방향을 무시했을 때 컴포넌트가 여러 개 존재한다면 모든 정점이 우승할 수 있습니다.</p> <h3 id="18890-seollal">18890 Seollal</h3> <p>문제를 간단하게 요약하면, <code class="language-plaintext highlighter-rouge">O</code> 또는 <code class="language-plaintext highlighter-rouge">X</code>가 적혀 있는 격자 그래프에서 간선을 적당히 연결해 <code class="language-plaintext highlighter-rouge">O</code>으로만 구성된 트리를 만들어야 합니다. 이때 0-based 기준 $i+j$가 홀수인 정점만 리프 정점이 될 수 있습니다. 또한, $(0,0)$이 루트 정점이며, 루트 정점은 차수가 1이더라도 리프 정점으로 취급하지 않습니다.</p> <p>루트가 아니면서 리프가 될 수 없는 정점의 차수는 항상 2 이상입니다. 그러므로 $i+j$가 짝수인 정점의 차수가 2인 포레스트를 구한 뒤, 트리가 될 때까지 다른 간선을 추가하면 문제를 해결할 수 있습니다. 만약 $i+j$가 짝수인 정점의 차수가 2인 포레스트를 구할 수 없다면 <code class="language-plaintext highlighter-rouge">NO</code>를 출력하면 됩니다.</p> <p>특정 정점의 차수가 2인 포레스트를 구하는 것은 Graphic Matroid와 Partition Matroid의 교집합을 구하는 것과 동일합니다. 그러므로 “$i+j$가 짝수의 정점 개수의 2배”와 “두 매트로이드의 교집합의 크기”가 동일하지 않다면 <code class="language-plaintext highlighter-rouge">NO</code>를 출력하고, 동일하면 트리가 될 때까지 다른 간선을 추가하면 됩니다.</p>JusticeHui서론 2022년 설날을 기념해 BOJ에서 민속놀이 관련 문제들을 풀어보았습니다.Good Bye 20212021-12-31T00:10:00+00:002021-12-31T00:10:00+00:00https://justicehui.github.io/2021/12/31/good-bye-2021<h2 id="서론">서론</h2> <p>3년 간의 고등학교 생활을 마치고 성인이 된 컴퓨터 전공하는 평범한 1학년 학생의 이야기입니다. 작년부터 전세계의 생활 패턴을 뒤바꾼 COVID-19 덕분에 대학 새내기 생활을 즐기지 못하는 저를 걱정하는 사람들이 종종 있던데, 방 안에 앉아서 하루 종일 컴퓨터만 하는 생활을 너무 좋아하는 저에게는 너무 행복한 시기였습니다.<br /> 월 단위로 구분해서 적을지 아니면 이벤트 단위로 구분해서 적을지 고민을 했었는데, 월 단위로 구분해서 3월까지 적다보니 너무 구린 것 같아서 다 지우고 이벤트 단위로 작성합니다.</p> <h2 id="2021년을-맞이할-준비">2021년을 맞이할 준비</h2> <blockquote> <p>제목은 뭔가 대단한 이야기를 할 것 같지만 그냥 Good Bye, BOJ 2020 이야기입니다.</p> </blockquote> <p>2020년 12월 31일 19:00부터 22:30까지 BOJ에서 <a href="https://www.acmicpc.net/contest/view/578">Good Bye, BOJ 2020</a>이 진행되었습니다. 저는 대회 출제진으로 참가했기 때문에 <a href="https://www.twitch.tv/gravekper">Gravekper</a>님 방송에 참가해 제가 출제한 문제를 풀이하면서 2021년이 다가오는 것을 기다리고 있었습니다.</p> <p>비교적 난이도가 낮은 전반부(ABCDE) 문제의 해설과 스코어보드 공개를 마쳤을 때 23시 40분 정도 되었던 것으로 기억하는데, 운영진과 참가자들이 각자 원하는 방식으로 2021년을 맞이할 수 있도록 약 30분 정도 쉬어가는 시간을 가졌습니다.</p> <p><strong>드디어 2021년이 되었습니다!</strong> 가족들과 함께 잠깐의 시간을 보낸 뒤 다시 대회 풀이 방송으로 복귀했습니다. 성인이 된 기념으로 맥주도 한 병 마셨습니다. 제가 출제한 또 다른 문제를 풀이해야 하는데 술을 마셔서 그런지 정신이 없네요. 취해서(?) 횡설수설하는 모습이 Good Bye, BOJ 2020! 풀이 방송에 <a href="https://youtu.be/qX8qPIcigjo?t=406">박제</a>되었습니다. 저스티스휘가 아니라 주(酒)스티스휘인가? 올해 1월 1일과 12월 25일에 한 번씩 술을 마셨는데, 두 번 중 한 번이 유튜브에 박제되어서 당황스럽네요…</p> <h3 id="2021년-목표-확인">2021년 목표 확인</h3> <p>올해 초에 몇 가지 목표를 세웠는데 많이 달성하지 못한 것 같아서 아쉽습니다.</p> <ul> <li>BOJ 5000문제 (성공) <ul> <li>6000문제를 풀었습니다.</li> </ul> </li> <li>solved.ac Master (성공) <ul> <li>solved.ac가 경험치 체제에서 AC Rating 체제로 바뀐 이후, 레이팅이 신경쓰여서 문제를 풀 의욕이 없어졌습니다. 레이팅을 신경쓰고 싶지 않아도 계속 신경쓰이길래, 풀이를 아는 모든 루비 문제를 풀어서 Master를 찍었습니다. 그 이후로는 레이팅 신경 안 쓰면서 다시 재미있게 문제 풀고 있습니다.</li> </ul> </li> <li>Codeforces 2400 (실패) <ul> <li>작년 7월 이후로 한 번도 참여를 안 했고, 내년부터는 다시 열심히 할 생각입니다. 가끔 심심할 때 문제를 조금씩 풀어보는데 아직 감이 많이 떨어지지 않은 것 같아서 다행인 것 같습니다.</li> </ul> </li> <li>AtCoder 2000 (실패) <ul> <li>주말마다 계속 다른 할 일이 있어서 거의 참가를 하지 못했습니다. 꾸준히 치면 2000은 갈 것 같은데… ABC가 8문제가 된 이후로 재미없어서 안 하고 있습니다.</li> </ul> </li> <li>ICPC / SCPC 수상 (실패) <ul> <li>상을 받을 수 있을 것 같다고 예상했던 ICPC와 SCPC는 상을 못 받았고, 대신 상을 못 받을 줄 알았던 UCPC에서 상을 받았습니다.</li> </ul> </li> </ul> <h2 id="학교-생활">학교 생활</h2> <h4 id="연구실">연구실</h4> <p>11월에 숭실대학교 합격 발표가 나서 <a href="/about/">블로그 자기 소개 페이지</a>에 숭실대학교를 적어놓았는데, 입학도 하기 전인 1월에 우연히 제 블로그에 들어온 교수님께서 해당 페이지를 보시고 학부 연구생을 제안하셨습니다. 이렇게 해서 신입생 OT와 입학보다 학부 연구생을 먼저 하게 되는 놀라운 경험을 했습니다.</p> <p>6개월 동안 일하는 것으로 계약했습니다. 처음에는 의욕을 갖고 논문도 읽어보고, 입학 전이라서 시간이 많아 코드도 어느정도 작성했습니다. 하지만 개학을 하니 과제랑 대회 준비에 치이면서 시간을 거의 투자하지 못했습니다. 지식이 부족한 것도 한 몫 했고요. 계약 기간이 끝나고 돌아보니 많이 기여를 못한 것이 아쉽습니다.</p> <h4 id="입학-전">입학 전</h4> <p>마지막으로 아무 생각 없이 놀 수 있는 시기라서 마음껏 놀고 싶었지만, 슬프게도 고등학교 3년 내내 PS만 했기 때문에 노는 방법도 잘 모릅니다. 결국 <strong>‘입학하기 전에 BOJ에서 4000문제를 풀자’</strong> 라는 생각에 한 달 동안 열심히 랭작을 했습니다. SCCC에 스카우트 돼서 1월에 겨울방학 스터디에도 참여했습니다.</p> <p>신입생 OT 도중에 “특기자로 들어왔을 것 같은 관상이다” 라는 말을 들은 것도 기억나네요. 무슨 느낌인지 잘 알아서 딱히 반박은 못하겠습니다. 이런 이야기를 들으면 기분 나쁜 사람들도 있는데, 저는 중학생 때부터 많이 들은 이야기라서 그런지 별 감흥이 없습니다.</p> <h4 id="1학기">1학기</h4> <p>입학을 했고, 1학기에 21.5학점을 신청했습니다. 전공 과목들은 전부 고등학교에서 배운 내용들이라 부담이 적었고, 교양은 딱히 학점을 챙기고자 하는 의욕이 없어서 공부를 안 했기 때문에 결국 고등학생 때와 마찬가지로 PS만 했습니다. 절대평가 덕분에 학점은 괜찮게 나왔습니다.</p> <h4 id="2학기">2학기</h4> <p>1학년은 1년 동안 40학점만 들을 수 있다고 하더군요. 마침 전공기초 + 교양필수가 18.5학점이라서 그것만 신청했습니다. 1학기와 마찬가지로 학점을 챙기고자 하는 의욕이 없어서 또 PS만 했습니다. 심지어 확률과 통계 기말고사는 시험 범위 1/3도 공부하지 않은 채로 시험을 보러 갔습니다.<br />내년부터는 공부 좀 해야 하는데… 고3 때도 공부 안 했던 거 생각하면 제가 공부를 할지 의문이네요.</p> <h2 id="대회-참가">대회 참가</h2> <p>시간 순서대로 나열합니다. 문제 이야기는 적지 않습니다.<br />UCPC에서 수상하고 SCPC와 현대 모비스 본선 진출할 때까지만 해도 일이 잘 풀리는 느낌이었는데 SCPC 본선과 ICPC를 완전히 망쳤습니다. 내년에는 잘할 수 있을지 불안하네요…</p> <h4 id="google-hash-code">Google Hash Code</h4> <p>열심히 팀원을 찾아보니 <strong>cs71107 / jhnah917 / klimmek55 / wookje</strong>라는 괜찮은 구성의 팀을 만들었습니다. 팀 이름은 매우 정직하게 <strong>wookje7110755917</strong>로 지었습니다. klimmek55님의 버스 운전 실력 덕분에 제 실력에 비해 매우 높은 한국 7등이라는 성과를 거두었습니다. 이런 대회는 어떻게 준비해야 할지 잘 모르겠네요… 너무 어려웠습니다.</p> <h4 id="reply-code-challenge-teen-edition">Reply Code Challenge Teen Edition</h4> <p><a href="/review/2021/03/09/reply-code-challenge-2020/">2020년에 참가</a>해서 망했던 대회에 올해 또 참가했습니다. 20살이지만 아직 만 나이로 10대라서 Teen Edition에 참가할 수 있더군요. 팀원은 선린 4명이 뭉친 <strong>hnsk2109 / jhnah917 / junseo / solsam10</strong>이고, 팀 이름은 작년과 동일하게 <strong>No Reply Team</strong>입니다. 작년에는 한 명이 혹사당하는 형태로 대회가 진행되었는데, 올해는 Codeforces Master가 2명 있어서 부담을 분산시킬 수 있었습니다.</p> <p>4번 문제가 Half Plane Intersection의 넓이를 구하는 문제라서 날로 먹을 생각을 했는데, 데이터가 잘못된 건지 모든 팀이 Hidden Test를 맞추지 못했습니다. 만약 맞았다면 순위가 많이 올라갔을 것 같아서 아쉽습니다.</p> <p>1923팀 중 18등, 특히 한국 팀 중에는 2등을 기록했습니다. 작년보다 순위가 많이 올라서 기뻤습니다.</p> <h4 id="ucpc">UCPC</h4> <p>이왕이면 UCPC 멤버로 ICPC까지 같이 참가하는 것이 좋을 것 같아 교내에서 팀원을 구했습니다. SCCC 슬랙에서 열심히 팀원을 구한 결과, Codeforces Candidate Master 2명과 함께 참가를 하게 되었습니다. 팀원은 <strong>enochjung / jhnah917 / lobo_prix</strong>이고 팀 이름은 <strong>AC-Complete</strong>입니다.</p> <p>예선은 무슨 짓을 해도 통과한다는 확신이 있어서 각자 집에서 참가하기로 했습니다. 프리즈 전에 8문제를 풀었고, 프리즈 동안 한 문제도 풀지 못하면서 17등으로 마무리했습니다. 이것보다 더 좋은 성적을 낼 수 없다고 느껴질 만큼 괜찮았다고 생각했지만 17등 밖에 못한 것을 보고, 본선에서 수상을 못할 것 같다는 예감이 들었습니다.</p> <p>본선은 학교에 모여서 참가했습니다. 프리즈 전에 6문제를 풀었고, 예선과 마찬가지로 프리즈 이후로 한 문제도 풀지 못하면서 17등으로 마무리했습니다. 정말로 예선보다 더 좋은 성적을 내지는 못했습니다. 15등까지 수상 대상이기 때문에 많이 슬펐지만, 16 ~ 21등에게 주는 특별상을 받아서 다행이었습니다. 은퇴/휴학 팀을 생각했을 때, 열심히 준비하면 ICPC에서도 수상을 할 수 있다는 느낌이 들었습니다.</p> <h4 id="scpc">SCPC</h4> <p>1차 예선은 대충 풀었고, 2차 예선은 열심히 풀어서 본선에 진출했습니다. (<a href="/review/2021/08/08/scpc-qual/">예선 후기</a>)</p> <p>본선 후기가 블로그에 올라가지 않은 것을 보면 알 수 있지만, 본선을 말아먹으면서 수상을 못했습니다. 작년 기출을 토대로 전략을 세웠는데, 올해 1번이 작년 1번에 비해 많이 어려웠던 탓에 전략이 꼬이면서 망했습니다. 내년에는 상을 받을 수 있을까요?</p> <h4 id="현대-모비스-알고리즘-경진대회">현대 모비스 알고리즘 경진대회</h4> <p>예선 상위 50등이 본선에 진출하고, 본선은 50등까지 상을 주는 대회입니다. 즉, 예선만 통과하면 본선에서 0점을 받아도 상을 받을 수 있습니다.</p> <p>예선은 시간 제한 관련해서 이슈가 있었습니다. 마지막 문제에서 맞는 것 같은 코드를 제출해도 TLE가 발생하는 이슈가 있었는데, 지금까지 PS를 하면서 터득한 상수 커팅 테크닉을 열심히 사용해서 겨우 만점을 받았습니다. 만점자는 전부 본선에 진출했고, 만점자가 아닌 사람은 1~2명 정도 본선에 간 것으로 알고 있습니다.</p> <p>본선 문제는 예선에 비해 많이 어려웠습니다. 결국 부분 점수만 긁다가 대회가 끝났고, 5등상을 받게 되었습니다.</p> <h4 id="icpc">ICPC</h4> <p>UCPC와 동일하게 AC-Complete 팀으로 참가했습니다. 예선은 교내 2등만 하자는 마인드로 편하게 참여했습니다. 실제로 대회 당시 끝나기 1시간 전에 교내 2등을 확정지은 뒤에는 풀이가 나온 문제가 있지만 구현하기 귀찮아서 안 하고 그냥 놀았습니다. 덕분에 5솔브라는 처참한 결과와 함께 주변 사람들에게 무수한 놀림을 받았습니다…</p> <p>본선은 대회 초반에 꽤 순위가 높았던 것으로 기억하는데 EFG를 모두 못 풀어서 결국 수상권에 들지 못했습니다. 내년에는 누구랑 나가는 것이 좋을지 모르겠네요.</p> <h2 id="대회-운영">대회 운영</h2> <p>총 21개의 대회 운영에 참여했습니다. 원래는 대회 별로 짧게 후기를 쓰려고 했는데, 정리를 해보니 너무 많아서 기억에 남는 대회만 몇 개 적었습니다.</p> <ul> <li>BOJ 오리지널 대회 <ul> <li>Good Bye, BOJ 2021</li> <li>제3회 IDT Cup / Semi-Game Cup 2 / Orange Cup</li> </ul> </li> <li>대학교 교내 대회 <ul> <li><strong>카이스트</strong> 봄 대회</li> <li><strong>고려대학교</strong> 교내 프로그래밍 대회 / 정보대학-정보보호학부 교류전 / 알고리즘 동아리 내부 대회</li> <li><strong>강원대학교</strong> / <strong>연세대학교 미래캠퍼스</strong> / <strong>서강대학교</strong> / <strong>한양대학교</strong> / <strong>인천대학교</strong> 교내 프로그래밍 대회</li> </ul> </li> <li>고등학교 교내 대회 <ul> <li><strong>선린인터넷고등학교</strong> 알고리즘 페스티벌 예선 / 알고리즘 페스티벌 본선 / 알고리즘 경시대회 / 가을맞이 알고리즘 대회</li> <li><strong>경기과학고등학교</strong> / <strong>경기북과학고등학교</strong> (2번) / <strong>세종과학예술영재학교</strong> 교내 프로그래밍 대회</li> </ul> </li> </ul> <h4 id="각종-선린-교내-대회">각종 선린 교내 대회</h4> <p>알고리즘 페스티벌(천하제일)과 알고리즘 경시대회에 20문제 중 13문제를 출제하고 17문제의 데이터를 만들었습니다. 졸업하고 출제하려고 2년 전부터 열심히 모은 문제 아이디어를 전부 쏟아내었습니다. 문제를 세팅하다보니 난이도 분포가 망한 것 같아서 불안했는데, 스코어보드가 이쁘게 나와서 기분 좋았습니다. 출제 후기는 <a href="/review/2021/08/20/sunrin-icpc-2021/">여기</a>에서 볼 수 있습니다.<br />가을맞이 대회는 전부 2학년 재학생들이 출제했습니다. 저는 지문 수정, 데이터 제작, 데이터 검증 위주로 검수를 했습니다. Beginner Division 문제 난이도 분포가 조금 아쉬웠습니다.</p> <h4 id="각종-고려대학교-관련-대회">각종 고려대학교 관련 대회</h4> <p><strong>ALPS 5월 내부 대회</strong>에서는 문제 검수가 아닌 서버 관리를 했습니다. 이때 <a href="https://github.com/cms-dev/cms">CMS</a>로 직접 대회를 열면서 <a href="/etc/2021/05/18/cms-1/">CMS 세팅 튜토리얼</a>을 블로그에 올렸습니다. 튜토리얼 2편 아직도 안 올렸는데 언제 올릴지 모르겠습니다.<br /><strong>정보대학-정보보호학부 교류전</strong>는 문제 검수와 서버 관리, 그리고 각종 잡무를 수행했습니다. 마라톤 매치 형식의 대회를 운영하는 것은 처음이라 대회 형식(특히 제출 방식)에 대해 많이 고민했는데, 결국 (시간 제한 3초 / 코드 제출) + (제출 간격 1분) + (Pretest / Systest) + (마지막 제출만 반영)으로 결정했습니다. Pretest를 도입해서 대회 중의 서버 부담을 줄이고, 마지막 제출만 반영하기 때문에 시스템 테스트도 힘들지 않았습니다.<br /><strong>KCPC</strong>는 대회 2일 전에 검수로 투입되었습니다. 원래는 마지막 문제를 검수하기 위해서 들어갔지만, 30시간 동안 고민했는데 못 풀고 다른 문제들만 검수했습니다… 풀이는 뻔한데 구현은 도저히 못하겠네요ㅠㅠ</p> <h4 id="idt-cup">IDT Cup</h4> <p>지인들과 카페에서 음료를 마시던 도중, 갑자기 IDT Cup 출제자가 카페로 찾아와서 검수진으로 납치해갔습니다. 저는 대부분의 시간을 <a href="https://www.acmicpc.net/problem/21731">BOJ 21731 메시라이브</a>에 투자해서 데이터를 강화시켰습니다. Dinic’s Algorithm에 Capacity Scaling을 적용한 코드, Push-Relabel Algorithm을 각종 휴리스틱으로 최적화한 코드 등 대회 시간에 생각할 수 있는 대부분의 방법을 모두 막았습니다.<br />대회가 끝나고 5달이 지난 올해 10월에 서로 다른 3가지 알고리즘을 조합해서 AC를 받을 수 있는 코드를 만들긴 했는데, 저는 양심적인 PS러이기 때문에 제출은 하지 않았습니다. 이건 어떻게 막아야 할까요? 막을 수 있는 방법이 존재하긴 하나…</p> <h4 id="semi-game-cup-2">Semi-Game Cup 2</h4> <p>검수진에 저보다 잘하는 사람들이 많이 있어서, 저는 문제 풀이 검수가 아닌 Validator / Checker / Interactor 등을 검수했습니다. 검수 날로 먹고 운영진 상품까지 받아서 기분 좋았습니다.</p> <h4 id="good-bye-boj-2021">Good Bye, BOJ 2021</h4> <p>2019, 2020년에 이어서 올해도 운영진이 되었습니다. 원래 출제를 하려고 했지만, 아이디어가 다 떨어져서 결국 출제를 하지 못했습니다. 이 글을 올릴 때만 해도 이렇게 루팡을 할 줄 알았지만…</p> <p>D번과 G번 문제 출제자분들이 풀이 영상을 녹화하지 못하는 상황이라 제가 하게 되었습니다. D번은 3번 정도, G번은 10번 정도 녹음했습니다. 덕분에 2021년 12월 31일과 2022년 1월 1일에 많은 BOJ 유저들이 강제로 제 목소리를 듣게 되었습니다.<br /> 대회 뒷풀이 방송에서 스코어보드를 오픈할 때, 스팟보드의 특정 등수부터 오픈하는 기능이 비효율적으로 구현되어 있어서 방송 진행을 하시는 분의 컴퓨터에서 안 돌아가는 상황이 발생했습니다. 다행히 미리 제 컴퓨터에서 켜놓았어서 급하게 팀뷰어를 켜서 진행했습니다.</p> <p>루팡하려고 마음을 나쁘게 먹어서 그런지 벌을 받은 기분입니다. Hello, BOJ 2022에서는 열심히 일하겠습니다.</p> <h2 id="2021년을-마치며">2021년을 마치며…</h2> <h4 id="2022년-목표">2022년 목표</h4> <p>마지막으로, 내년 목표와 계획을 세울 차례입니다.<br />지금까지는 PS만 했지만, 이제 슬슬 다른 분야도 공부를 할 필요를 느끼고 있습니다. 고등학교 때의 경험에 의하면 개발은 별로 재미 없는 것 같아, 그나마 PS와 관련이 있는 분야부터 조금씩 확장을 해보려고 합니다.</p> <ul> <li>Problem Solving <ul> <li>BOJ 8000문제</li> <li>Codeforces 2400</li> <li>AtCoder 2000</li> <li>ICPC/SCPC 수상</li> <li>APIO/IOI Call for Tasks에 낼 문제 생각하기</li> </ul> </li> <li>Non-PS <ul> <li>각종 유량 알고리즘 공부 (Orlin, Parallel Push-Relabel, Scaling 등)</li> <li>Professional CUDA C Programming 읽기</li> <li>추천 알고리즘 공부하고 CUDA로 구현해보기(?)</li> <li>병렬 처리 공부하기</li> </ul> </li> </ul> <h4 id="마무리">마무리</h4> <p>인터넷을 통해 얻은 지식을 다시 인터넷에 돌려주겠다는 생각으로 블로그를 시작했었는데, 올해 들어 공부와 아르바이트를 핑계로 블로그에 글을 거의 올리지 않았습니다. 아르바이트(?)를 통해 얻은 강의 실력을 통해 내년에는 좋은 글을 많이 올릴 수 있도록 노력하겠습니다.</p>JusticeHui서론 3년 간의 고등학교 생활을 마치고 성인이 된 컴퓨터 전공하는 평범한 1학년 학생의 이야기입니다. 작년부터 전세계의 생활 패턴을 뒤바꾼 COVID-19 덕분에 대학 새내기 생활을 즐기지 못하는 저를 걱정하는 사람들이 종종 있던데, 방 안에 앉아서 하루 종일 컴퓨터만 하는 생활을 너무 좋아하는 저에게는 너무 행복한 시기였습니다. 월 단위로 구분해서 적을지 아니면 이벤트 단위로 구분해서 적을지 고민을 했었는데, 월 단위로 구분해서 3월까지 적다보니 너무 구린 것 같아서 다 지우고 이벤트 단위로 작성합니다.SIMD in PS - ICPC Seoul Regional 2021 L. Trio2021-11-15T05:24:00+00:002021-11-15T05:24:00+00:00https://justicehui.github.io/hard-algorithm/2021/11/15/simd-in-ps<h3 id="서론">서론</h3> <p>2021년 서울 리저널 L번 문제로 출제된 Trio는 다양한 풀이가 존재합니다. 의도된 풀이는 포함 배제를 사용하는 $O(N^2\cdot 81)$ 정도의 풀이로 추측되지만 $O(N^3)$에 비례하는 풀이, $O(N^2 \cdot 10\,000)$를 bitset으로 최적화한 등 다양한 풀이가 나왔습니다. 이 글에서는 SIMD를 이용한 두 가지 풀이를 알아보면서 PS에 SIMD를 어떻게 적용할 수 있을지 살펴보겠습니다.</p> <h3 id="simd란">SIMD란?</h3> <p>자세한 내용은 다루지 않고, SIMD가 무엇인지 알 수 있을 정도로만 정리하고 넘어가겠습니다.</p> <p>SIMD는 Single Instruction Multiple Data의 약자로, 하나의 명령어로 여러 데이터에 대해 동일한 연산을 <strong>동시에</strong> 수행하는 방식입니다. 예를 들어 아래 코드는 8번의 덧셈 연산을 수행해야 하지만, 뒤에서 설명할 avx를 이용하면 한 번의 명령으로 8번의 덧셈을 동시에 수행할 수 있습니다.</p> <div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">int</span> <span class="n">a</span><span class="p">[</span><span class="mi">8</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span><span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">5</span><span class="p">,</span> <span class="mi">6</span><span class="p">,</span> <span class="mi">7</span><span class="p">},</span> <span class="n">b</span><span class="p">[</span><span class="mi">8</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">1</span><span class="p">},</span> <span class="n">c</span><span class="p">[</span><span class="mi">8</span><span class="p">];</span> <span class="k">for</span><span class="p">(</span><span class="kt">int</span> <span class="n">i</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span> <span class="n">i</span><span class="o">&lt;</span><span class="mi">8</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="n">c</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="n">a</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">+</span> <span class="n">b</span><span class="p">[</span><span class="n">i</span><span class="p">];</span> <span class="c1">// c = { 1, 3, 5, 7, 8, 8, 8, 8 }</span> </code></pre></div></div> <p>Codeforces에서 고인물들의 코드를 구경하다 보면 <code class="language-plaintext highlighter-rouge">#pragma GCC target("sse,sse2,sse3,ssse3,sse4,avx,avx2")</code> 와 같은 코드를 심심치 않게 볼 수 있습니다. sse와 avx 같은 것들은 SIMD 명령어셋으로, sse 시리즈는 128bit 단위, avx 시리즈는 256bit 단위로 병렬 처리를 할 수 있습니다. 즉, avx 명령어셋을 사용하면 int형(32bit) 덧셈 8번을 동시에 할 수 있습니다.<br />이런 명령어셋의 지원 여부는 CPU마다 다르기 때문에 컴파일러는 기본적으로 SIMD 명령어를 사용하지 않도록 컴파일합니다. 위 코드는 해당 명령어셋을 사용해서 컴파일하도록 컴파일러에게 지시하는 구문입니다. 만약 컴파일 옵션을 직접 입력할 수 있다면 <code class="language-plaintext highlighter-rouge">-msse -mavx -mavx2</code>와 같이 옵션을 줘도 됩니다. 아래 코드는 avx를 이용해서 길이가 8인 두 배열의 덧셈을 수행하는 코드입니다.</p> <div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#pragma GCC target ("avx2") #include &lt;bits/stdc++.h&gt; #include &lt;x86intrin.h&gt; </span><span class="k">using</span> <span class="k">namespace</span> <span class="n">std</span><span class="p">;</span> <span class="k">alignas</span><span class="p">(</span><span class="mi">32</span><span class="p">)</span> <span class="kt">int</span> <span class="n">a</span><span class="p">[</span><span class="mi">8</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span><span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">5</span><span class="p">,</span> <span class="mi">6</span><span class="p">,</span> <span class="mi">7</span><span class="p">},</span> <span class="n">b</span><span class="p">[</span><span class="mi">8</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">1</span><span class="p">},</span> <span class="n">c</span><span class="p">[</span><span class="mi">8</span><span class="p">];</span> <span class="kt">int</span> <span class="nf">main</span><span class="p">(){</span> <span class="n">ios_base</span><span class="o">::</span><span class="n">sync_with_stdio</span><span class="p">(</span><span class="nb">false</span><span class="p">);</span> <span class="n">cin</span><span class="p">.</span><span class="n">tie</span><span class="p">(</span><span class="nb">nullptr</span><span class="p">);</span> <span class="n">__m256i</span> <span class="n">av</span> <span class="o">=</span> <span class="n">_mm256_load_si256</span><span class="p">((</span><span class="k">const</span> <span class="n">__m256i</span><span class="o">*</span><span class="p">)</span><span class="n">a</span><span class="p">);</span> <span class="n">__m256i</span> <span class="n">bv</span> <span class="o">=</span> <span class="n">_mm256_load_si256</span><span class="p">((</span><span class="k">const</span> <span class="n">__m256i</span><span class="o">*</span><span class="p">)</span><span class="n">b</span><span class="p">);</span> <span class="n">__m256i</span> <span class="n">cv</span> <span class="o">=</span> <span class="n">_mm256_add_epi32</span><span class="p">(</span><span class="n">av</span><span class="p">,</span> <span class="n">bv</span><span class="p">);</span> <span class="n">_mm256_store_si256</span><span class="p">((</span><span class="n">__m256i</span><span class="o">*</span><span class="p">)</span><span class="n">c</span><span class="p">,</span> <span class="n">cv</span><span class="p">);</span> <span class="k">for</span><span class="p">(</span><span class="kt">int</span> <span class="n">i</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span> <span class="n">i</span><span class="o">&lt;</span><span class="mi">8</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">c</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">&lt;&lt;</span> <span class="s">" "</span><span class="p">;</span> <span class="p">}</span> </code></pre></div></div> <p><code class="language-plaintext highlighter-rouge">__m256i</code>는 정수 자료형(char, short, int , long long 등)을 저장하는 256bit 크기의 벡터입니다. <code class="language-plaintext highlighter-rouge">__mm256_load_si256</code>은 align 되어 있으면서 메모리에서 연속한 256bit 를 가져오는 함수이고, <code class="language-plaintext highlighter-rouge">__m256_add_epi32</code>는 두 <code class="language-plaintext highlighter-rouge">__m256i</code>를 32bit 기준으로 더하는 함수입니다. load 명령이 <strong>메모리에서 연속</strong>한 데이터를 가져온다는 것에 주목해주세요.</p> <p>사실 요즘 컴파일러는 똑똑해서 위 코드처럼 직접 SIMD 명령을 작성하지 않더라도, target을 지정해주고 O3 최적화 옵션을 주면 적당히 잘 컴파일해줍니다.</p> <p>SIMD 명령어셋 별로 지원하는 연산은 <a href="https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html">Intel Intrinsics Guide</a>에서 확인할 수 있고, 자신의 CPU에서 지원하는 명령어셋은 <a href="https://www.cpuid.com/softwares/cpu-z.html">CPU-Z</a>를 이용해 확인할 수 있습니다. 참고로 BOJ는 AVX2까지 지원하고, ICPC Seoul Regional은 AVX2를 지원하는 것은 확인했는데 AVX512는 확인하지 못했습니다.</p> <h3 id="on396">$O(N^3/96)$</h3> <p>가장 먼저 떠올릴 수 있는 Naive한 풀이는 $i &lt; j &lt; k$를 잡아서 $(A_i, A_j), (A_j, A_k), (A_k, A_i)$의 공통 부분이 모두 같은지 확인하는 $O(N^3)$ 풀이입니다. $C(i, j)$를 $(A_i, A_j)$의 공통 부분이라고 정의하면 $C(i, j) = C(j, k) = C(k, i)$가 모두 동일한지 확인하면 됩니다. $i &lt; j &lt; k$ 조건이 붙어있기 때문에 실제로 확인하는 $(i,j,k)$ 튜플의 개수는 대략 $N^3/6$입니다.</p> <div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include &lt;bits/stdc++.h&gt; </span><span class="k">using</span> <span class="k">namespace</span> <span class="n">std</span><span class="p">;</span> <span class="kt">int</span> <span class="n">N</span><span class="p">,</span> <span class="n">C</span><span class="p">[</span><span class="mi">2000</span><span class="p">][</span><span class="mi">2020</span><span class="p">];</span> <span class="n">string</span> <span class="n">A</span><span class="p">[</span><span class="mi">2000</span><span class="p">];</span> <span class="kt">long</span> <span class="kt">long</span> <span class="n">R</span><span class="p">;</span> <span class="kt">int</span> <span class="nf">main</span><span class="p">(){</span> <span class="n">ios_base</span><span class="o">::</span><span class="n">sync_with_stdio</span><span class="p">(</span><span class="nb">false</span><span class="p">);</span> <span class="n">cin</span><span class="p">.</span><span class="n">tie</span><span class="p">(</span><span class="nb">nullptr</span><span class="p">);</span> <span class="n">cin</span> <span class="o">&gt;&gt;</span> <span class="n">N</span><span class="p">;</span> <span class="k">for</span><span class="p">(</span><span class="kt">int</span> <span class="n">i</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span> <span class="n">i</span><span class="o">&lt;</span><span class="n">N</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="n">cin</span> <span class="o">&gt;&gt;</span> <span class="n">A</span><span class="p">[</span><span class="n">i</span><span class="p">];</span> <span class="k">for</span><span class="p">(</span><span class="kt">int</span> <span class="n">i</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span> <span class="n">i</span><span class="o">&lt;</span><span class="n">N</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">){</span> <span class="k">for</span><span class="p">(</span><span class="kt">int</span> <span class="n">j</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span> <span class="n">j</span><span class="o">&lt;</span><span class="n">i</span><span class="p">;</span> <span class="n">j</span><span class="o">++</span><span class="p">){</span> <span class="kt">int</span> <span class="n">now</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="k">for</span><span class="p">(</span><span class="kt">int</span> <span class="n">k</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span> <span class="n">k</span><span class="o">&lt;</span><span class="mi">4</span><span class="p">;</span> <span class="n">k</span><span class="o">++</span><span class="p">){</span> <span class="n">now</span> <span class="o">=</span> <span class="n">now</span> <span class="o">*</span> <span class="mi">10</span> <span class="o">+</span> <span class="p">(</span><span class="n">A</span><span class="p">[</span><span class="n">i</span><span class="p">][</span><span class="n">k</span><span class="p">]</span> <span class="o">==</span> <span class="n">A</span><span class="p">[</span><span class="n">j</span><span class="p">][</span><span class="n">k</span><span class="p">]</span> <span class="o">?</span> <span class="n">A</span><span class="p">[</span><span class="n">i</span><span class="p">][</span><span class="n">k</span><span class="p">]</span> <span class="o">-</span> <span class="sc">'0'</span> <span class="o">:</span> <span class="mi">0</span><span class="p">);</span> <span class="p">}</span> <span class="n">C</span><span class="p">[</span><span class="n">i</span><span class="p">][</span><span class="n">j</span><span class="p">]</span> <span class="o">=</span> <span class="n">C</span><span class="p">[</span><span class="n">j</span><span class="p">][</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="n">now</span><span class="p">;</span> <span class="p">}</span> <span class="p">}</span> <span class="k">for</span><span class="p">(</span><span class="kt">int</span> <span class="n">i</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span> <span class="n">i</span><span class="o">&lt;</span><span class="n">N</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">){</span> <span class="k">for</span><span class="p">(</span><span class="kt">int</span> <span class="n">j</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span> <span class="n">j</span><span class="o">&lt;</span><span class="n">i</span><span class="p">;</span> <span class="n">j</span><span class="o">++</span><span class="p">){</span> <span class="kt">int</span> <span class="n">key</span> <span class="o">=</span> <span class="n">C</span><span class="p">[</span><span class="n">i</span><span class="p">][</span><span class="n">j</span><span class="p">];</span> <span class="k">for</span><span class="p">(</span><span class="kt">int</span> <span class="n">k</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span> <span class="n">k</span><span class="o">&lt;</span><span class="n">j</span><span class="p">;</span> <span class="n">k</span><span class="o">++</span><span class="p">)</span> <span class="n">R</span> <span class="o">+=</span> <span class="n">key</span> <span class="o">==</span> <span class="n">C</span><span class="p">[</span><span class="n">i</span><span class="p">][</span><span class="n">k</span><span class="p">]</span> <span class="o">&amp;&amp;</span> <span class="n">key</span> <span class="o">==</span> <span class="n">C</span><span class="p">[</span><span class="n">j</span><span class="p">][</span><span class="n">k</span><span class="p">];</span> <span class="p">}</span> <span class="p">}</span> <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">R</span><span class="p">;</span> <span class="p">}</span> </code></pre></div></div> <p>이때 23번째 줄을 잘 보시면 <code class="language-plaintext highlighter-rouge">key</code>와 비교하는 데이터들이 메모리에서 연속하기 때문에 SIMD를 적용할 수 있습니다. 또한 모든 수는 32768보다 작기 때문에 short형을 써도 무방하고, 16번의 연산을 동시에 처리할 수 있으므로 시간 복잡도는 $O(N^3/6/16) = O(N^3/96)$이 됩니다. 위 코드에 O3 옵션과 AVX2 타겟만 지정해도 됩니다.</p> <div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#pragma GCC optimize ("O3") #pragma GCC target ("avx2") #include &lt;bits/stdc++.h&gt; #include &lt;x86intrin.h&gt; </span><span class="k">using</span> <span class="k">namespace</span> <span class="n">std</span><span class="p">;</span> <span class="k">alignas</span><span class="p">(</span><span class="mi">32</span><span class="p">)</span> <span class="kt">short</span> <span class="n">N</span><span class="p">,</span> <span class="n">C</span><span class="p">[</span><span class="mi">2000</span><span class="p">][</span><span class="mi">2000</span><span class="p">];</span> <span class="n">string</span> <span class="n">A</span><span class="p">[</span><span class="mi">2000</span><span class="p">];</span> <span class="kt">long</span> <span class="kt">long</span> <span class="n">R</span><span class="p">;</span> <span class="kt">void</span> <span class="nf">Solve</span><span class="p">(</span><span class="kt">int</span> <span class="n">u</span><span class="p">,</span> <span class="kt">int</span> <span class="n">v</span><span class="p">){</span> <span class="k">static</span> <span class="kt">short</span> <span class="n">tmp</span><span class="p">[</span><span class="mi">16</span><span class="p">],</span> <span class="n">i</span><span class="p">;</span> <span class="n">__m256i</span> <span class="n">key</span> <span class="o">=</span> <span class="n">_mm256_set1_epi16</span><span class="p">(</span><span class="n">C</span><span class="p">[</span><span class="n">u</span><span class="p">][</span><span class="n">v</span><span class="p">]);</span> <span class="n">__m256i</span> <span class="n">res</span> <span class="o">=</span> <span class="n">_mm256_setzero_si256</span><span class="p">();</span> <span class="k">for</span><span class="p">(</span><span class="n">i</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span> <span class="n">i</span><span class="o">+</span><span class="mi">16</span><span class="o">&lt;=</span><span class="n">u</span><span class="p">;</span> <span class="n">i</span><span class="o">+=</span><span class="mi">16</span><span class="p">){</span> <span class="n">__m256i</span> <span class="n">now_u</span> <span class="o">=</span> <span class="n">_mm256_load_si256</span><span class="p">((</span><span class="k">const</span> <span class="n">__m256i</span><span class="o">*</span><span class="p">)(</span><span class="n">C</span><span class="p">[</span><span class="n">u</span><span class="p">]</span><span class="o">+</span><span class="n">i</span><span class="p">));</span> <span class="c1">// C[u][i] ~ C[u][i+15]</span> <span class="n">__m256i</span> <span class="n">now_v</span> <span class="o">=</span> <span class="n">_mm256_load_si256</span><span class="p">((</span><span class="k">const</span> <span class="n">__m256i</span><span class="o">*</span><span class="p">)(</span><span class="n">C</span><span class="p">[</span><span class="n">v</span><span class="p">]</span><span class="o">+</span><span class="n">i</span><span class="p">));</span> <span class="c1">// C[v][i] ~ C[v][i+15]</span> <span class="n">now_u</span> <span class="o">=</span> <span class="n">_mm256_cmpeq_epi16</span><span class="p">(</span><span class="n">now_u</span><span class="p">,</span> <span class="n">key</span><span class="p">);</span> <span class="c1">// C[u][k] == key</span> <span class="n">now_v</span> <span class="o">=</span> <span class="n">_mm256_cmpeq_epi16</span><span class="p">(</span><span class="n">now_v</span><span class="p">,</span> <span class="n">key</span><span class="p">);</span> <span class="c1">// C[v][k] == key</span> <span class="n">now_v</span> <span class="o">=</span> <span class="n">_mm256_and_si256</span><span class="p">(</span><span class="n">now_v</span><span class="p">,</span> <span class="n">now_u</span><span class="p">);</span> <span class="c1">// C[u][k] == key &amp;&amp; C[v][k] == key</span> <span class="n">res</span> <span class="o">=</span> <span class="n">_mm256_sub_epi16</span><span class="p">(</span><span class="n">res</span><span class="p">,</span> <span class="n">now_v</span><span class="p">);</span> <span class="c1">// res[0] += now_v[0], res[1] += now_v[1], ...</span> <span class="p">}</span> <span class="k">while</span><span class="p">(</span><span class="n">i</span> <span class="o">&lt;</span> <span class="n">u</span><span class="p">)</span> <span class="n">R</span> <span class="o">+=</span> <span class="n">C</span><span class="p">[</span><span class="n">u</span><span class="p">][</span><span class="n">v</span><span class="p">]</span> <span class="o">==</span> <span class="n">C</span><span class="p">[</span><span class="n">u</span><span class="p">][</span><span class="n">i</span><span class="p">]</span> <span class="o">&amp;&amp;</span> <span class="n">C</span><span class="p">[</span><span class="n">u</span><span class="p">][</span><span class="n">v</span><span class="p">]</span> <span class="o">==</span> <span class="n">C</span><span class="p">[</span><span class="n">v</span><span class="p">][</span><span class="n">i</span><span class="p">],</span> <span class="n">i</span><span class="o">++</span><span class="p">;</span> <span class="c1">// 16개씩 처리하고 남은 원소들 처리 (최대 15개)</span> <span class="n">_mm256_store_si256</span><span class="p">((</span><span class="n">__m256i</span><span class="o">*</span><span class="p">)</span><span class="n">tmp</span><span class="p">,</span> <span class="n">res</span><span class="p">);</span> <span class="k">for</span><span class="p">(</span><span class="n">i</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span> <span class="n">i</span><span class="o">&lt;</span><span class="mi">16</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="n">R</span> <span class="o">+=</span> <span class="n">tmp</span><span class="p">[</span><span class="n">i</span><span class="p">];</span> <span class="p">}</span> <span class="kt">int</span> <span class="nf">main</span><span class="p">(){</span> <span class="n">ios_base</span><span class="o">::</span><span class="n">sync_with_stdio</span><span class="p">(</span><span class="nb">false</span><span class="p">);</span> <span class="n">cin</span><span class="p">.</span><span class="n">tie</span><span class="p">(</span><span class="nb">nullptr</span><span class="p">);</span> <span class="n">cin</span> <span class="o">&gt;&gt;</span> <span class="n">N</span><span class="p">;</span> <span class="k">for</span><span class="p">(</span><span class="kt">int</span> <span class="n">i</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span> <span class="n">i</span><span class="o">&lt;</span><span class="n">N</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="n">cin</span> <span class="o">&gt;&gt;</span> <span class="n">A</span><span class="p">[</span><span class="n">i</span><span class="p">];</span> <span class="k">for</span><span class="p">(</span><span class="kt">int</span> <span class="n">i</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span> <span class="n">i</span><span class="o">&lt;</span><span class="n">N</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">){</span> <span class="k">for</span><span class="p">(</span><span class="kt">int</span> <span class="n">j</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span> <span class="n">j</span><span class="o">&lt;</span><span class="n">i</span><span class="p">;</span> <span class="n">j</span><span class="o">++</span><span class="p">){</span> <span class="kt">int</span> <span class="n">now</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="k">for</span><span class="p">(</span><span class="kt">int</span> <span class="n">k</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span> <span class="n">k</span><span class="o">&lt;</span><span class="mi">4</span><span class="p">;</span> <span class="n">k</span><span class="o">++</span><span class="p">){</span> <span class="n">now</span> <span class="o">=</span> <span class="n">now</span> <span class="o">*</span> <span class="mi">10</span> <span class="o">+</span> <span class="p">(</span><span class="n">A</span><span class="p">[</span><span class="n">i</span><span class="p">][</span><span class="n">k</span><span class="p">]</span> <span class="o">==</span> <span class="n">A</span><span class="p">[</span><span class="n">j</span><span class="p">][</span><span class="n">k</span><span class="p">]</span> <span class="o">?</span> <span class="n">A</span><span class="p">[</span><span class="n">i</span><span class="p">][</span><span class="n">k</span><span class="p">]</span> <span class="o">-</span> <span class="sc">'0'</span> <span class="o">:</span> <span class="mi">0</span><span class="p">);</span> <span class="p">}</span> <span class="n">C</span><span class="p">[</span><span class="n">i</span><span class="p">][</span><span class="n">j</span><span class="p">]</span> <span class="o">=</span> <span class="n">C</span><span class="p">[</span><span class="n">j</span><span class="p">][</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="n">now</span><span class="p">;</span> <span class="p">}</span> <span class="p">}</span> <span class="k">for</span><span class="p">(</span><span class="kt">int</span> <span class="n">i</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span> <span class="n">i</span><span class="o">&lt;</span><span class="n">N</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="k">for</span><span class="p">(</span><span class="kt">int</span> <span class="n">j</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span> <span class="n">j</span><span class="o">&lt;</span><span class="n">i</span><span class="p">;</span> <span class="n">j</span><span class="o">++</span><span class="p">)</span> <span class="n">Solve</span><span class="p">(</span><span class="n">j</span><span class="p">,</span> <span class="n">i</span><span class="p">);</span> <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">R</span><span class="p">;</span> <span class="p">}</span> </code></pre></div></div> <p>기본적인 흐름은 다음과 같습니다.</p> <ol> <li><code class="language-plaintext highlighter-rouge">C[u][v] == C[u][i]</code>를 SIMD로 계산 (<code class="language-plaintext highlighter-rouge">now_u</code>)</li> <li><code class="language-plaintext highlighter-rouge">C[u][v] == C[v][i]</code>를 SIMD로 계산 (<code class="language-plaintext highlighter-rouge">now_v</code>)</li> <li><code class="language-plaintext highlighter-rouge">now_v</code>와 <code class="language-plaintext highlighter-rouge">now_u</code>를 AND 연산</li> <li>정답에 반영</li> </ol> <p><code class="language-plaintext highlighter-rouge">_mm256_cmpeq_epi16</code>은 다르면 <code class="language-plaintext highlighter-rouge">0000...</code>, 같으면 <code class="language-plaintext highlighter-rouge">1111...</code>을 반환하기 때문에 true를 -1로 취급할 수 있으므로 <code class="language-plaintext highlighter-rouge">res</code>에 <code class="language-plaintext highlighter-rouge">_mm256_sub_epi16</code>을 취합니다.</p> <h3 id="on2cdot-20000512">$O(N^2\cdot 20000/512)$</h3> <p>이 풀이는 제가 대회 중에 작성한 풀이로, $N^3$풀이보다 조금 복잡합니다. bitset을 알고 있다고 가정하고 제 풀이를 먼저 설명합니다.</p> <p>수 2개를 고정하면 각 자리는 “무조건 x가 와야 하는자리” / “y z 빼고 아무거나 와도 되는 자리” 중 하나에 해당합니다. 예를 들어 고정된 수가 1234, 1243이라면 1/2번째 자리에는 무조건 1/2가 와야 하고, 3/4번째 자리에는 3/4 빼고 아무거나 와도 됩니다.<br />즉, 12?? 꼴을 만족하는 수와 { ??3?, ???4, ??4?, ???3 } 꼴을 만족하지 않는 수의 교집합의 크기를 구하면 됩니다. 구현의 편의를 위해 0을 와일드 카드 문자로 취급합시다.</p> <p><code class="language-plaintext highlighter-rouge">B[i][j]</code>를 j가 패턴 i와 매칭되면 1, 안 되면 0으로 정의합시다. <code class="language-plaintext highlighter-rouge">bitset&lt;10000&gt; B[10000]</code>처럼 bitset을 이용하면 위 예시는 <code class="language-plaintext highlighter-rouge">B[1200] &amp; ~B[30] &amp; ~B[4] &amp; ~B[40] &amp; ~B[3]</code>으로 구할 수 있고, 최대 8번의 AND 연산을 사용하므로 시간 복잡도는 $O(N^2 \cdot 10000 \cdot 8 / 2w)$입니다. 이때 $w$는 word size(보통 64)이고, $N \choose 2$개의 pair만 보면 되므로 $/2$가 추가로 붙게 됩니다.<br />이때, <code class="language-plaintext highlighter-rouge">~B[x]</code>꼴에서 <code class="language-plaintext highlighter-rouge">x</code>에 와일드 카드가 아닌 인덱스가 하나 밖에 없다는 점을 이용해서 최적화를 할 수 있습니다.</p> <p><code class="language-plaintext highlighter-rouge">D[i][j]</code>를 j와 i가 한 자리 이상 겹치면 0, 안 겹치면 1으로 정의합시다. 예를 들어 1234라면 1xxx, x2xx, xx3x, xxx4, 총 9000개의 칸을 0으로 세팅합니다.<br />D 배열도 전처리해두면 위 예시는 <code class="language-plaintext highlighter-rouge">B[1200] &amp; D[34] &amp; D[43]</code>으로 구할 수 있고, AND 연산을 2번만 사용하므로 시간 복잡도는 $O(N^2 \cdot 10000\cdot 2 / 2w)$가 됩니다.</p> <p>bitset을 그냥 사용하면 $w=64$라서 $O(N^2\cdot 20000/128)$이지만, AVX를 사용하면 $w=256$이므로 시간 복잡도는 $O(N^2\cdot 20000/512)$가 됩니다. 아래 코드는 단순이 O3 옵션과 AVX2 타겟을 지정한 코드입니다.</p> <div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#pragma GCC optimize ("O3") #pragma GCC target ("avx2") #include &lt;bits/stdc++.h&gt; </span><span class="k">using</span> <span class="k">namespace</span> <span class="n">std</span><span class="p">;</span> <span class="k">alignas</span><span class="p">(</span><span class="mi">32</span><span class="p">)</span> <span class="kt">int</span> <span class="n">N</span><span class="p">,</span> <span class="n">A</span><span class="p">[</span><span class="mi">2000</span><span class="p">];</span> <span class="n">bitset</span><span class="o">&lt;</span><span class="mi">10000</span><span class="o">&gt;</span> <span class="n">B</span><span class="p">[</span><span class="mi">10000</span><span class="p">],</span> <span class="n">D</span><span class="p">[</span><span class="mi">10000</span><span class="p">];</span> <span class="kt">long</span> <span class="kt">long</span> <span class="n">R</span><span class="p">;</span> <span class="n">array</span><span class="o">&lt;</span><span class="kt">int</span><span class="p">,</span><span class="mi">4</span><span class="o">&gt;</span> <span class="n">Decomp</span><span class="p">(</span><span class="kt">int</span> <span class="n">n</span><span class="p">){</span> <span class="n">array</span><span class="o">&lt;</span><span class="kt">int</span><span class="p">,</span><span class="mi">4</span><span class="o">&gt;</span> <span class="n">arr</span><span class="p">;</span> <span class="n">arr</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="n">n</span> <span class="o">/</span> <span class="mi">1000</span> <span class="o">*</span> <span class="mi">1000</span><span class="p">;</span> <span class="n">n</span> <span class="o">%=</span> <span class="mi">1000</span><span class="p">;</span> <span class="n">arr</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="n">n</span> <span class="o">/</span> <span class="mi">100</span> <span class="o">*</span> <span class="mi">100</span><span class="p">;</span> <span class="n">n</span> <span class="o">%=</span> <span class="mi">100</span><span class="p">;</span> <span class="n">arr</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span> <span class="o">=</span> <span class="n">n</span> <span class="o">/</span> <span class="mi">10</span> <span class="o">*</span> <span class="mi">10</span><span class="p">;</span> <span class="n">n</span> <span class="o">%=</span> <span class="mi">10</span><span class="p">;</span> <span class="n">arr</span><span class="p">[</span><span class="mi">3</span><span class="p">]</span> <span class="o">=</span> <span class="n">n</span> <span class="o">/</span> <span class="mi">1</span> <span class="o">*</span> <span class="mi">1</span><span class="p">;</span> <span class="n">n</span> <span class="o">%=</span> <span class="mi">1</span><span class="p">;</span> <span class="k">return</span> <span class="n">arr</span><span class="p">;</span> <span class="p">}</span> <span class="kt">void</span> <span class="nf">Init</span><span class="p">(</span><span class="kt">int</span> <span class="n">n</span><span class="p">){</span> <span class="k">auto</span> <span class="n">arr</span> <span class="o">=</span> <span class="n">Decomp</span><span class="p">(</span><span class="n">n</span><span class="p">);</span> <span class="k">for</span><span class="p">(</span><span class="kt">int</span> <span class="n">bit</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span> <span class="n">bit</span><span class="o">&lt;</span><span class="mi">16</span><span class="p">;</span> <span class="n">bit</span><span class="o">++</span><span class="p">){</span> <span class="kt">int</span> <span class="n">now</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="k">for</span><span class="p">(</span><span class="kt">int</span> <span class="n">i</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span> <span class="n">i</span><span class="o">&lt;</span><span class="mi">4</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="k">if</span><span class="p">(</span><span class="n">bit</span> <span class="o">&amp;</span> <span class="p">(</span><span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="n">i</span><span class="p">))</span> <span class="n">now</span> <span class="o">+=</span> <span class="n">arr</span><span class="p">[</span><span class="n">i</span><span class="p">];</span> <span class="n">B</span><span class="p">[</span><span class="n">now</span><span class="p">].</span><span class="n">set</span><span class="p">(</span><span class="n">n</span><span class="p">);</span> <span class="p">}</span> <span class="k">for</span><span class="p">(</span><span class="kt">int</span> <span class="n">p</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span> <span class="n">p</span><span class="o">&lt;</span><span class="mi">4</span><span class="p">;</span> <span class="n">p</span><span class="o">++</span><span class="p">){</span> <span class="n">vector</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span> <span class="n">p10</span> <span class="o">=</span> <span class="p">{</span><span class="mi">1000</span><span class="p">,</span> <span class="mi">100</span><span class="p">,</span> <span class="mi">10</span><span class="p">,</span> <span class="mi">1</span><span class="p">};</span> <span class="n">p10</span><span class="p">.</span><span class="n">erase</span><span class="p">(</span><span class="n">p10</span><span class="p">.</span><span class="n">begin</span><span class="p">()</span> <span class="o">+</span> <span class="n">p</span><span class="p">);</span> <span class="k">for</span><span class="p">(</span><span class="kt">int</span> <span class="n">i</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span> <span class="n">i</span><span class="o">&lt;</span><span class="mi">10</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">){</span> <span class="k">for</span><span class="p">(</span><span class="kt">int</span> <span class="n">j</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span> <span class="n">j</span><span class="o">&lt;</span><span class="mi">10</span><span class="p">;</span> <span class="n">j</span><span class="o">++</span><span class="p">){</span> <span class="k">for</span><span class="p">(</span><span class="kt">int</span> <span class="n">k</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span> <span class="n">k</span><span class="o">&lt;</span><span class="mi">10</span><span class="p">;</span> <span class="n">k</span><span class="o">++</span><span class="p">){</span> <span class="kt">int</span> <span class="n">now</span> <span class="o">=</span> <span class="n">arr</span><span class="p">[</span><span class="n">p</span><span class="p">]</span> <span class="o">+</span> <span class="n">i</span> <span class="o">*</span> <span class="n">p10</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">+</span> <span class="n">j</span> <span class="o">*</span> <span class="n">p10</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">+</span> <span class="n">k</span> <span class="o">*</span> <span class="n">p10</span><span class="p">[</span><span class="mi">2</span><span class="p">];</span> <span class="n">D</span><span class="p">[</span><span class="n">now</span><span class="p">].</span><span class="n">reset</span><span class="p">(</span><span class="n">n</span><span class="p">);</span> <span class="p">}</span> <span class="p">}</span> <span class="p">}</span> <span class="p">}</span> <span class="p">}</span> <span class="kt">void</span> <span class="nf">Solve</span><span class="p">(</span><span class="kt">int</span> <span class="n">a</span><span class="p">,</span> <span class="kt">int</span> <span class="n">b</span><span class="p">){</span> <span class="k">auto</span> <span class="n">a_v</span> <span class="o">=</span> <span class="n">Decomp</span><span class="p">(</span><span class="n">a</span><span class="p">),</span> <span class="n">b_v</span> <span class="o">=</span> <span class="n">Decomp</span><span class="p">(</span><span class="n">b</span><span class="p">);</span> <span class="kt">int</span> <span class="n">mask</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span> <span class="n">am</span> <span class="o">=</span> <span class="n">a</span><span class="p">,</span> <span class="n">bm</span> <span class="o">=</span> <span class="n">b</span><span class="p">;</span> <span class="k">for</span><span class="p">(</span><span class="kt">int</span> <span class="n">i</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span> <span class="n">i</span><span class="o">&lt;</span><span class="mi">4</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="k">if</span><span class="p">(</span><span class="n">a_v</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">==</span> <span class="n">b_v</span><span class="p">[</span><span class="n">i</span><span class="p">])</span> <span class="n">mask</span> <span class="o">+=</span> <span class="n">a_v</span><span class="p">[</span><span class="n">i</span><span class="p">],</span> <span class="n">am</span> <span class="o">-=</span> <span class="n">a_v</span><span class="p">[</span><span class="n">i</span><span class="p">],</span> <span class="n">bm</span> <span class="o">-=</span> <span class="n">b_v</span><span class="p">[</span><span class="n">i</span><span class="p">];</span> <span class="k">auto</span> <span class="n">res</span> <span class="o">=</span> <span class="n">B</span><span class="p">[</span><span class="n">mask</span><span class="p">]</span> <span class="o">&amp;</span> <span class="n">D</span><span class="p">[</span><span class="n">am</span><span class="p">]</span> <span class="o">&amp;</span> <span class="n">D</span><span class="p">[</span><span class="n">bm</span><span class="p">];</span> <span class="n">R</span> <span class="o">+=</span> <span class="n">res</span><span class="p">.</span><span class="n">count</span><span class="p">()</span> <span class="o">-</span> <span class="n">res</span><span class="p">[</span><span class="n">a</span><span class="p">]</span> <span class="o">-</span> <span class="n">res</span><span class="p">[</span><span class="n">b</span><span class="p">];</span> <span class="p">}</span> <span class="kt">int</span> <span class="nf">main</span><span class="p">(){</span> <span class="n">ios_base</span><span class="o">::</span><span class="n">sync_with_stdio</span><span class="p">(</span><span class="nb">false</span><span class="p">);</span> <span class="n">cin</span><span class="p">.</span><span class="n">tie</span><span class="p">(</span><span class="nb">nullptr</span><span class="p">);</span> <span class="n">cin</span> <span class="o">&gt;&gt;</span> <span class="n">N</span><span class="p">;</span> <span class="k">for</span><span class="p">(</span><span class="kt">int</span> <span class="n">i</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span> <span class="n">i</span><span class="o">&lt;</span><span class="n">N</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="n">cin</span> <span class="o">&gt;&gt;</span> <span class="n">A</span><span class="p">[</span><span class="n">i</span><span class="p">];</span> <span class="k">for</span><span class="p">(</span><span class="kt">int</span> <span class="n">i</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span> <span class="n">i</span><span class="o">&lt;</span><span class="mi">10000</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="n">D</span><span class="p">[</span><span class="n">i</span><span class="p">].</span><span class="n">set</span><span class="p">();</span> <span class="k">for</span><span class="p">(</span><span class="kt">int</span> <span class="n">i</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span> <span class="n">i</span><span class="o">&lt;</span><span class="n">N</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="n">Init</span><span class="p">(</span><span class="n">A</span><span class="p">[</span><span class="n">i</span><span class="p">]);</span> <span class="k">for</span><span class="p">(</span><span class="kt">int</span> <span class="n">i</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span> <span class="n">i</span><span class="o">&lt;</span><span class="n">N</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="k">for</span><span class="p">(</span><span class="kt">int</span> <span class="n">j</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span> <span class="n">j</span><span class="o">&lt;</span><span class="n">i</span><span class="p">;</span> <span class="n">j</span><span class="o">++</span><span class="p">)</span> <span class="n">Solve</span><span class="p">(</span><span class="n">A</span><span class="p">[</span><span class="n">i</span><span class="p">],</span> <span class="n">A</span><span class="p">[</span><span class="n">j</span><span class="p">]);</span> <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">R</span> <span class="o">/</span> <span class="mi">3</span><span class="p">;</span> <span class="p">}</span> </code></pre></div></div> <p>AVX2로 직접 구현한 코드는 <a href="http://boj.kr/992042c380a14afa93930c91de85495b">여기</a>에서 확인하실 수 있습니다.</p>JusticeHui서론 2021년 서울 리저널 L번 문제로 출제된 Trio는 다양한 풀이가 존재합니다. 의도된 풀이는 포함 배제를 사용하는 $O(N^2\cdot 81)$ 정도의 풀이로 추측되지만 $O(N^3)$에 비례하는 풀이, $O(N^2 \cdot 10\,000)$를 bitset으로 최적화한 등 다양한 풀이가 나왔습니다. 이 글에서는 SIMD를 이용한 두 가지 풀이를 알아보면서 PS에 SIMD를 어떻게 적용할 수 있을지 살펴보겠습니다.백준16992 3-SAT2021-09-23T13:26:00+00:002021-09-23T13:26:00+00:00https://justicehui.github.io/ps/2021/09/23/BOJ16992<h3 id="문제-링크">문제 링크</h3> <ul> <li>http://icpc.me/16992</li> </ul> <h3 id="사용-알고리즘">사용 알고리즘</h3> <ul> <li>Simulated Annealing</li> </ul> <h3 id="풀이">풀이</h3> <p>3-SAT 문제는 NP-Complete에 속하므로 다항 시간 안에 해결하는 알고리즘이 알려져 있지 않습니다. 적절한 휴리스틱을 이용해 문제를 풀어봅시다.</p> <p>제 풀이는 기본적으로 Simulated Annealing을 사용합니다. SA에 대한 설명은 계절학교와 구글 해시코드에서 놀라운 휴리스틱 실력을 보여주었던 Ryute님의 설명으로 대신합니다.</p> <ul> <li><a href="https://ryute.tistory.com/35">PS: 뉴비를 위한 Simulated Annealing 입문 (1)</a></li> <li><a href="https://ryute.tistory.com/36">PS: 뉴비를 위한 Simulated Annealing 입문 (2)</a></li> </ul> <p>제가 처음 시도한 풀이는 다음과 같습니다.</p> <ol> <li>$N, M$이 매우 작으면($M\cdot2^N \leq 3\cdot10^8$정도?) Bruteforcing으로 답을 찾는다.</li> <li>최대한 다양한 상수(랜덤 시드, 볼츠만 상수, 초기 온도, 온도 변화율, 상태 전이 개수 등)를 이용해 Simulated Annealing을 시도한다.</li> <li>맞은 테스트케이스의 합집합이 최대한 많은 부분을 커버하도록 코드를 잘 조합한다.</li> </ol> <p>(3)은 이 문제가 BOJ에서 전체 채점 문제이고 채점 순서가 고정되어 있기 때문에 가능합니다. (3)을 실현하기 위해서는 똑같은 결과를 재현할 수 있어야 하므로, 모든 랜덤 시드는 손으로 직접 입력해야 합니다.</p> <p>여기까지 오면 19x점 정도를 얻을 수 있습니다. 초기 상태를 모두 false으로 지정해서 그런지 성능이 별로 안 좋았습니다. 그래서 초기 상태를 지정하는 것도 여러 가지 방법을 시도해 보았습니다.</p> <ul> <li>모두 false으로 초기화 (<code class="language-plaintext highlighter-rouge">ZeroInitializer</code>)</li> <li>랜덤으로 초기화 (<code class="language-plaintext highlighter-rouge">RandomInitializer</code>)</li> <li>clause에 true로 등장하는 횟수가 많으면 true, false로 등장하는 횟수가 더 많으면 false로 초기화 (<code class="language-plaintext highlighter-rouge">StrangeInitializer</code>)</li> </ul> <p>여러 초기화 전략을 시도하니 200 ~ 203점을 받게 되었습니다. 여기까지 작성한 코드는 다음과 같습니다. 앞서 말한 것처럼, 실행 결과를 재현할 수 있도록 랜덤 시드를 직접 파라미터로 넘깁니다.</p> <div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cm">/** * SimulatedAnnealing * @param n : number of variables * @param cl : clauses list * @param init : initial state * @param k : Boltzmann constant * @param t : initial temperature * @param delta : delta temperature * @param epoch : number of iterations * @param select : number of changes at one iteration * @param seed : random seed * @return : true if SA find the answer */</span> <span class="kt">bool</span> <span class="nf">SimulatedAnnealing</span><span class="p">(</span><span class="kt">int</span> <span class="n">n</span><span class="p">,</span> <span class="k">const</span> <span class="n">clauses</span> <span class="o">&amp;</span><span class="n">cl</span><span class="p">,</span> <span class="n">State</span> <span class="n">init</span><span class="p">,</span> <span class="kt">double</span> <span class="n">k</span><span class="p">,</span> <span class="kt">double</span> <span class="n">t</span><span class="p">,</span> <span class="kt">double</span> <span class="n">delta</span><span class="p">,</span> <span class="kt">int</span> <span class="n">epoch</span><span class="p">,</span> <span class="kt">int</span> <span class="n">select</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span> <span class="kt">int</span> <span class="n">seed</span><span class="o">=</span><span class="mh">0x917917</span><span class="p">){</span> <span class="n">State</span> <span class="n">now</span> <span class="o">=</span> <span class="n">init</span><span class="p">;</span> <span class="kt">int</span> <span class="n">score</span> <span class="o">=</span> <span class="n">now</span><span class="p">.</span><span class="n">score</span><span class="p">(</span><span class="n">cl</span><span class="p">);</span> <span class="n">Random</span> <span class="n">rnd</span><span class="p">(</span><span class="n">n</span><span class="p">,</span> <span class="n">seed</span><span class="p">);</span> <span class="k">for</span><span class="p">(</span><span class="kt">int</span> <span class="n">iter</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span> <span class="n">iter</span><span class="o">&lt;</span><span class="n">epoch</span><span class="p">;</span> <span class="n">iter</span><span class="o">++</span><span class="p">){</span> <span class="n">State</span> <span class="n">nxt</span> <span class="o">=</span> <span class="n">now</span><span class="p">;</span> <span class="k">for</span><span class="p">(</span><span class="kt">int</span> <span class="n">j</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span> <span class="n">j</span><span class="o">&lt;</span><span class="n">select</span><span class="p">;</span> <span class="n">j</span><span class="o">++</span><span class="p">)</span> <span class="n">nxt</span><span class="p">.</span><span class="n">flip</span><span class="p">(</span><span class="n">rnd</span><span class="p">);</span> <span class="kt">int</span> <span class="n">nxtScore</span> <span class="o">=</span> <span class="n">nxt</span><span class="p">.</span><span class="n">score</span><span class="p">(</span><span class="n">cl</span><span class="p">);</span> <span class="k">if</span><span class="p">(</span><span class="n">exp</span><span class="p">((</span><span class="kt">double</span><span class="p">)(</span><span class="n">nxtScore</span> <span class="o">-</span> <span class="n">score</span><span class="p">)</span> <span class="o">/</span> <span class="p">(</span><span class="n">k</span> <span class="o">*</span> <span class="n">t</span><span class="p">))</span> <span class="o">&gt;</span> <span class="n">rnd</span><span class="p">.</span><span class="n">next</span><span class="p">()){</span> <span class="n">now</span> <span class="o">=</span> <span class="n">nxt</span><span class="p">;</span> <span class="n">score</span> <span class="o">=</span> <span class="n">nxtScore</span><span class="p">;</span> <span class="p">}</span> <span class="k">if</span><span class="p">(</span><span class="n">score</span> <span class="o">==</span> <span class="n">cl</span><span class="p">.</span><span class="n">size</span><span class="p">()){</span> <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">"1</span><span class="se">\n</span><span class="s">"</span><span class="p">;</span> <span class="n">now</span><span class="p">.</span><span class="n">print</span><span class="p">(</span><span class="n">n</span><span class="p">);</span> <span class="k">return</span> <span class="nb">true</span><span class="p">;</span> <span class="p">}</span> <span class="n">t</span> <span class="o">*=</span> <span class="n">delta</span><span class="p">;</span> <span class="p">}</span> <span class="k">return</span> <span class="nb">false</span><span class="p">;</span> <span class="p">}</span> <span class="c1">// 중략</span> <span class="n">Random</span> <span class="nf">R</span><span class="p">(</span><span class="mh">0x917917</span><span class="p">);</span> <span class="c1">// Random Manager class using mt19937</span> <span class="k">if</span><span class="p">(</span><span class="n">SimulatedAnnealing</span><span class="p">(</span><span class="n">N</span><span class="p">,</span> <span class="n">C</span><span class="p">,</span> <span class="n">ZeroInitializer</span><span class="p">(</span><span class="n">N</span><span class="p">)</span> <span class="p">,</span> <span class="mf">1.5</span><span class="p">,</span> <span class="mf">1.0</span><span class="p">,</span> <span class="mf">0.9999</span><span class="p">,</span> <span class="mi">50505</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mh">0x482794</span><span class="p">))</span> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span> <span class="k">if</span><span class="p">(</span><span class="n">SimulatedAnnealing</span><span class="p">(</span><span class="n">N</span><span class="p">,</span> <span class="n">C</span><span class="p">,</span> <span class="n">ZeroInitializer</span><span class="p">(</span><span class="n">N</span><span class="p">)</span> <span class="p">,</span> <span class="mf">1.5</span><span class="p">,</span> <span class="mf">1.0</span><span class="p">,</span> <span class="mf">0.9999</span><span class="p">,</span> <span class="mi">50505</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mh">0x917917</span><span class="p">))</span> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span> <span class="k">if</span><span class="p">(</span><span class="n">SimulatedAnnealing</span><span class="p">(</span><span class="n">N</span><span class="p">,</span> <span class="n">C</span><span class="p">,</span> <span class="n">ZeroInitializer</span><span class="p">(</span><span class="n">N</span><span class="p">)</span> <span class="p">,</span> <span class="mf">2.5</span><span class="p">,</span> <span class="mf">1.0</span><span class="p">,</span> <span class="mf">0.9999</span><span class="p">,</span> <span class="mi">50505</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mh">0x482794</span><span class="p">))</span> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span> <span class="k">if</span><span class="p">(</span><span class="n">SimulatedAnnealing</span><span class="p">(</span><span class="n">N</span><span class="p">,</span> <span class="n">C</span><span class="p">,</span> <span class="n">ZeroInitializer</span><span class="p">(</span><span class="n">N</span><span class="p">)</span> <span class="p">,</span> <span class="mf">1.5</span><span class="p">,</span> <span class="mf">1.0</span><span class="p">,</span> <span class="mf">0.9999</span><span class="p">,</span> <span class="mi">50505</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mh">0x917917</span><span class="p">))</span> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span> <span class="k">if</span><span class="p">(</span><span class="n">SimulatedAnnealing</span><span class="p">(</span><span class="n">N</span><span class="p">,</span> <span class="n">C</span><span class="p">,</span> <span class="n">ZeroInitializer</span><span class="p">(</span><span class="n">N</span><span class="p">)</span> <span class="p">,</span> <span class="mf">1.5</span><span class="p">,</span> <span class="mf">1.0</span><span class="p">,</span> <span class="mf">0.9995</span><span class="p">,</span> <span class="mi">50505</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mh">0x917917</span><span class="p">))</span> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span> <span class="k">if</span><span class="p">(</span><span class="n">SimulatedAnnealing</span><span class="p">(</span><span class="n">N</span><span class="p">,</span> <span class="n">C</span><span class="p">,</span> <span class="n">RandomInitializer</span><span class="p">(</span><span class="n">N</span><span class="p">,</span> <span class="n">R</span><span class="p">)</span> <span class="p">,</span> <span class="mf">1.5</span><span class="p">,</span> <span class="mf">5.0</span><span class="p">,</span> <span class="mf">0.9999</span><span class="p">,</span> <span class="mi">50505</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mh">0x917917</span><span class="p">))</span> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span> <span class="k">if</span><span class="p">(</span><span class="n">SimulatedAnnealing</span><span class="p">(</span><span class="n">N</span><span class="p">,</span> <span class="n">C</span><span class="p">,</span> <span class="n">RandomInitializer</span><span class="p">(</span><span class="n">N</span><span class="p">,</span> <span class="n">R</span><span class="p">)</span> <span class="p">,</span> <span class="mf">1.5</span><span class="p">,</span> <span class="mf">5.0</span><span class="p">,</span> <span class="mf">0.9995</span><span class="p">,</span> <span class="mi">50505</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mh">0x917917</span><span class="p">))</span> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span> <span class="k">if</span><span class="p">(</span><span class="n">SimulatedAnnealing</span><span class="p">(</span><span class="n">N</span><span class="p">,</span> <span class="n">C</span><span class="p">,</span> <span class="n">StrangeInitializer</span><span class="p">(</span><span class="n">N</span><span class="p">,</span> <span class="n">C</span><span class="p">),</span> <span class="mf">1.0</span><span class="p">,</span> <span class="mf">1.5</span><span class="p">,</span> <span class="mf">0.9999</span><span class="p">,</span> <span class="mi">50505</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mh">0x917917</span><span class="p">))</span> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span> </code></pre></div></div> <p>여기까지 오면 보통 28, 41, 128번 테스트케이스 같은 곳에서 한 두 개 정도 틀릴텐데, 이런 부분을 직접 잡는 것은 너무 힘들어서 랜덤을 믿기로 했습니다.<br /><code class="language-plaintext highlighter-rouge">RandomInitializer</code>를 이용해 초기화를 한 뒤, 처음으로 false가 되는 clause에 관여하는 변수 하나를 뒤집는 연산(<code class="language-plaintext highlighter-rouge">flipHeuristic</code>)을 100번 정도 수행하는 것을 시간 제한이 끝나기 직전까지 반복합니다.</p> <div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">bool</span> <span class="nf">RandomSearch</span><span class="p">(</span><span class="kt">int</span> <span class="n">n</span><span class="p">,</span> <span class="k">const</span> <span class="n">clauses</span> <span class="o">&amp;</span><span class="n">cl</span><span class="p">,</span> <span class="kt">int</span> <span class="n">select</span><span class="p">,</span> <span class="kt">double</span> <span class="n">tl</span><span class="p">,</span> <span class="kt">int</span> <span class="n">seed</span><span class="o">=</span><span class="mh">0x917917</span><span class="p">){</span> <span class="n">Random</span> <span class="n">rnd</span><span class="p">(</span><span class="n">seed</span><span class="p">);</span> <span class="k">while</span><span class="p">(</span><span class="n">clock</span><span class="p">()</span> <span class="o">&lt;</span> <span class="n">tl</span> <span class="o">*</span> <span class="n">CLOCKS_PER_SEC</span><span class="p">){</span> <span class="n">State</span> <span class="n">now</span> <span class="o">=</span> <span class="n">RandomInitializer</span><span class="p">(</span><span class="n">n</span><span class="p">,</span> <span class="n">rnd</span><span class="p">);</span> <span class="k">for</span><span class="p">(</span><span class="kt">int</span> <span class="n">i</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span> <span class="n">i</span><span class="o">&lt;</span><span class="n">select</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">){</span> <span class="k">if</span><span class="p">(</span><span class="n">now</span><span class="p">.</span><span class="n">flipHeuristic</span><span class="p">(</span><span class="n">rnd</span><span class="p">,</span> <span class="n">cl</span><span class="p">)){</span> <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">"1</span><span class="se">\n</span><span class="s">"</span><span class="p">;</span> <span class="n">now</span><span class="p">.</span><span class="n">print</span><span class="p">(</span><span class="n">n</span><span class="p">);</span> <span class="k">return</span> <span class="nb">true</span><span class="p">;</span> <span class="p">}</span> <span class="p">}</span> <span class="p">}</span> <span class="k">return</span> <span class="nb">false</span><span class="p">;</span> <span class="p">}</span> <span class="c1">// 중략</span> <span class="k">if</span><span class="p">(</span><span class="n">RandomSearch</span><span class="p">(</span><span class="n">N</span><span class="p">,</span> <span class="n">C</span><span class="p">,</span> <span class="mi">100</span><span class="p">,</span> <span class="mf">1.5</span><span class="p">,</span> <span class="mh">0x814814</span><span class="p">))</span> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span> </code></pre></div></div> <p><code class="language-plaintext highlighter-rouge">RandomSearch</code>까지 추가했더니 204개의 테스트케이스를 모두 맞을 수 있었습니다.</p> <p>아래 코드는 랜덤 시드가 고정되어있기 때문에, 그대로 제출하면 글을 업로드한 2021년 9월 기준으로 모든 테스트케이스에 대해 올바른 답을 낼 수 있음이 보장됩니다.</p> <h3 id="전체-코드">전체 코드</h3> <div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include &lt;bits/stdc++.h&gt; </span><span class="k">using</span> <span class="k">namespace</span> <span class="n">std</span><span class="p">;</span> <span class="k">using</span> <span class="n">clause</span> <span class="o">=</span> <span class="n">array</span><span class="o">&lt;</span><span class="kt">int</span><span class="p">,</span><span class="mi">3</span><span class="o">&gt;</span><span class="p">;</span> <span class="k">using</span> <span class="n">clauses</span> <span class="o">=</span> <span class="n">vector</span><span class="o">&lt;</span><span class="n">clause</span><span class="o">&gt;</span><span class="p">;</span> <span class="k">struct</span> <span class="nc">Random</span><span class="p">{</span> <span class="n">mt19937</span> <span class="n">gen</span><span class="p">;</span> <span class="n">uniform_int_distribution</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span> <span class="n">rnd_int</span><span class="p">;</span> <span class="n">uniform_real_distribution</span><span class="o">&lt;</span><span class="kt">double</span><span class="o">&gt;</span> <span class="n">rnd_real</span><span class="p">;</span> <span class="n">Random</span><span class="p">(</span><span class="kt">int</span> <span class="n">n</span><span class="p">,</span> <span class="kt">int</span> <span class="n">seed</span><span class="o">=</span><span class="mh">0x917917</span><span class="p">)</span> <span class="o">:</span> <span class="n">gen</span><span class="p">(</span><span class="n">seed</span><span class="p">),</span> <span class="n">rnd_int</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="n">n</span><span class="p">),</span> <span class="n">rnd_real</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span> <span class="p">{}</span> <span class="kt">int</span> <span class="n">nextInt</span><span class="p">(){</span> <span class="k">return</span> <span class="n">rnd_int</span><span class="p">(</span><span class="n">gen</span><span class="p">);</span> <span class="p">}</span> <span class="kt">double</span> <span class="n">next</span><span class="p">(){</span> <span class="k">return</span> <span class="n">rnd_real</span><span class="p">(</span><span class="n">gen</span><span class="p">);</span> <span class="p">}</span> <span class="p">};</span> <span class="k">struct</span> <span class="nc">State</span><span class="p">{</span> <span class="n">array</span><span class="o">&lt;</span><span class="kt">int</span><span class="p">,</span> <span class="mi">111</span><span class="o">&gt;</span> <span class="n">a</span><span class="p">;</span> <span class="kt">int</span><span class="o">&amp;</span> <span class="k">operator</span> <span class="p">[]</span> <span class="p">(</span><span class="kt">int</span> <span class="n">idx</span><span class="p">)</span> <span class="p">{</span> <span class="k">return</span> <span class="n">a</span><span class="p">[</span><span class="n">idx</span><span class="p">];</span> <span class="p">}</span> <span class="kt">void</span> <span class="n">flip</span><span class="p">(</span><span class="n">Random</span> <span class="o">&amp;</span><span class="n">rnd</span><span class="p">){</span> <span class="n">a</span><span class="p">[</span><span class="n">rnd</span><span class="p">.</span><span class="n">nextInt</span><span class="p">()]</span> <span class="o">^=</span> <span class="mi">1</span><span class="p">;</span> <span class="p">}</span> <span class="kt">bool</span> <span class="n">flipHeuristic</span><span class="p">(</span><span class="n">Random</span> <span class="o">&amp;</span><span class="n">rnd</span><span class="p">,</span> <span class="k">const</span> <span class="n">clauses</span> <span class="o">&amp;</span><span class="n">cl</span><span class="p">){</span> <span class="k">for</span><span class="p">(</span><span class="k">const</span> <span class="k">auto</span> <span class="o">&amp;</span><span class="p">[</span><span class="n">x</span><span class="p">,</span><span class="n">y</span><span class="p">,</span><span class="n">z</span><span class="p">]</span> <span class="o">:</span> <span class="n">cl</span><span class="p">){</span> <span class="kt">bool</span> <span class="n">now</span> <span class="o">=</span> <span class="nb">false</span><span class="p">;</span> <span class="n">now</span> <span class="o">|=</span> <span class="n">x</span> <span class="o">&gt;</span> <span class="mi">0</span> <span class="o">?</span> <span class="n">a</span><span class="p">[</span><span class="n">x</span><span class="p">]</span> <span class="o">:</span> <span class="o">!</span><span class="n">a</span><span class="p">[</span><span class="o">-</span><span class="n">x</span><span class="p">];</span> <span class="n">now</span> <span class="o">|=</span> <span class="n">y</span> <span class="o">&gt;</span> <span class="mi">0</span> <span class="o">?</span> <span class="n">a</span><span class="p">[</span><span class="n">y</span><span class="p">]</span> <span class="o">:</span> <span class="o">!</span><span class="n">a</span><span class="p">[</span><span class="o">-</span><span class="n">y</span><span class="p">];</span> <span class="n">now</span> <span class="o">|=</span> <span class="n">z</span> <span class="o">&gt;</span> <span class="mi">0</span> <span class="o">?</span> <span class="n">a</span><span class="p">[</span><span class="n">z</span><span class="p">]</span> <span class="o">:</span> <span class="o">!</span><span class="n">a</span><span class="p">[</span><span class="o">-</span><span class="n">z</span><span class="p">];</span> <span class="k">if</span><span class="p">(</span><span class="o">!</span><span class="n">now</span><span class="p">){</span> <span class="kt">int</span> <span class="n">select</span> <span class="o">=</span> <span class="n">rnd</span><span class="p">.</span><span class="n">gen</span><span class="p">()</span> <span class="o">%</span> <span class="mi">3</span><span class="p">;</span> <span class="k">if</span><span class="p">(</span><span class="n">select</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="n">a</span><span class="p">[</span><span class="n">abs</span><span class="p">(</span><span class="n">x</span><span class="p">)]</span> <span class="o">^=</span> <span class="mi">1</span><span class="p">;</span> <span class="k">if</span><span class="p">(</span><span class="n">select</span> <span class="o">==</span> <span class="mi">1</span><span class="p">)</span> <span class="n">a</span><span class="p">[</span><span class="n">abs</span><span class="p">(</span><span class="n">y</span><span class="p">)]</span> <span class="o">^=</span> <span class="mi">1</span><span class="p">;</span> <span class="k">if</span><span class="p">(</span><span class="n">select</span> <span class="o">==</span> <span class="mi">2</span><span class="p">)</span> <span class="n">a</span><span class="p">[</span><span class="n">abs</span><span class="p">(</span><span class="n">z</span><span class="p">)]</span> <span class="o">^=</span> <span class="mi">1</span><span class="p">;</span> <span class="k">return</span> <span class="nb">false</span><span class="p">;</span> <span class="p">}</span> <span class="p">}</span> <span class="k">return</span> <span class="nb">true</span><span class="p">;</span> <span class="p">}</span> <span class="kt">int</span> <span class="n">score</span><span class="p">(</span><span class="k">const</span> <span class="n">clauses</span> <span class="o">&amp;</span><span class="n">cl</span><span class="p">)</span> <span class="k">const</span> <span class="p">{</span> <span class="kt">int</span> <span class="n">ret</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="k">for</span><span class="p">(</span><span class="k">const</span> <span class="k">auto</span> <span class="o">&amp;</span><span class="p">[</span><span class="n">x</span><span class="p">,</span><span class="n">y</span><span class="p">,</span><span class="n">z</span><span class="p">]</span> <span class="o">:</span> <span class="n">cl</span><span class="p">){</span> <span class="kt">bool</span> <span class="n">now</span> <span class="o">=</span> <span class="nb">false</span><span class="p">;</span> <span class="n">now</span> <span class="o">|=</span> <span class="n">x</span> <span class="o">&gt;</span> <span class="mi">0</span> <span class="o">?</span> <span class="n">a</span><span class="p">[</span><span class="n">x</span><span class="p">]</span> <span class="o">:</span> <span class="o">!</span><span class="n">a</span><span class="p">[</span><span class="o">-</span><span class="n">x</span><span class="p">];</span> <span class="n">now</span> <span class="o">|=</span> <span class="n">y</span> <span class="o">&gt;</span> <span class="mi">0</span> <span class="o">?</span> <span class="n">a</span><span class="p">[</span><span class="n">y</span><span class="p">]</span> <span class="o">:</span> <span class="o">!</span><span class="n">a</span><span class="p">[</span><span class="o">-</span><span class="n">y</span><span class="p">];</span> <span class="n">now</span> <span class="o">|=</span> <span class="n">z</span> <span class="o">&gt;</span> <span class="mi">0</span> <span class="o">?</span> <span class="n">a</span><span class="p">[</span><span class="n">z</span><span class="p">]</span> <span class="o">:</span> <span class="o">!</span><span class="n">a</span><span class="p">[</span><span class="o">-</span><span class="n">z</span><span class="p">];</span> <span class="n">ret</span> <span class="o">+=</span> <span class="n">now</span><span class="p">;</span> <span class="p">}</span> <span class="k">return</span> <span class="n">ret</span><span class="p">;</span> <span class="p">}</span> <span class="kt">void</span> <span class="n">print</span><span class="p">(</span><span class="kt">int</span> <span class="n">n</span><span class="p">)</span> <span class="k">const</span> <span class="p">{</span> <span class="k">for</span><span class="p">(</span><span class="kt">int</span> <span class="n">i</span><span class="o">=</span><span class="mi">1</span><span class="p">;</span> <span class="n">i</span><span class="o">&lt;=</span><span class="n">n</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">a</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">&lt;&lt;</span> <span class="s">" </span><span class="se">\n</span><span class="s">"</span><span class="p">[</span><span class="n">i</span><span class="o">==</span><span class="n">n</span><span class="p">];</span> <span class="p">}</span> <span class="p">};</span> <span class="n">State</span> <span class="nf">ZeroInitializer</span><span class="p">(</span><span class="kt">int</span> <span class="n">n</span><span class="p">){</span> <span class="n">State</span> <span class="n">ret</span><span class="p">;</span> <span class="k">for</span><span class="p">(</span><span class="kt">int</span> <span class="n">i</span><span class="o">=</span><span class="mi">1</span><span class="p">;</span> <span class="n">i</span><span class="o">&lt;=</span><span class="n">n</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="n">ret</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="k">return</span> <span class="n">ret</span><span class="p">;</span> <span class="p">}</span> <span class="n">State</span> <span class="nf">RandomInitializer</span><span class="p">(</span><span class="kt">int</span> <span class="n">n</span><span class="p">,</span> <span class="n">Random</span> <span class="o">&amp;</span><span class="n">rnd</span><span class="p">){</span> <span class="n">State</span> <span class="n">ret</span><span class="p">;</span> <span class="k">for</span><span class="p">(</span><span class="kt">int</span> <span class="n">i</span><span class="o">=</span><span class="mi">1</span><span class="p">;</span> <span class="n">i</span><span class="o">&lt;=</span><span class="n">n</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="n">ret</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="n">rnd</span><span class="p">.</span><span class="n">gen</span><span class="p">()</span> <span class="o">&amp;</span> <span class="mi">1</span><span class="p">;</span> <span class="k">return</span> <span class="n">ret</span><span class="p">;</span> <span class="p">}</span> <span class="n">State</span> <span class="nf">StrangeInitializer</span><span class="p">(</span><span class="kt">int</span> <span class="n">n</span><span class="p">,</span> <span class="k">const</span> <span class="n">clauses</span> <span class="o">&amp;</span><span class="n">cl</span><span class="p">){</span> <span class="n">State</span> <span class="n">ret</span><span class="p">;</span> <span class="n">vector</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span> <span class="n">sum</span><span class="p">(</span><span class="n">n</span><span class="o">+</span><span class="mi">1</span><span class="p">);</span> <span class="k">for</span><span class="p">(</span><span class="k">const</span> <span class="k">auto</span> <span class="o">&amp;</span><span class="n">arr</span> <span class="o">:</span> <span class="n">cl</span><span class="p">){</span> <span class="k">for</span><span class="p">(</span><span class="k">const</span> <span class="k">auto</span> <span class="o">&amp;</span><span class="n">j</span> <span class="o">:</span> <span class="n">arr</span><span class="p">)</span> <span class="n">sum</span><span class="p">[</span><span class="n">abs</span><span class="p">(</span><span class="n">j</span><span class="p">)]</span> <span class="o">+=</span> <span class="n">j</span><span class="p">;</span> <span class="p">}</span> <span class="k">for</span><span class="p">(</span><span class="kt">int</span> <span class="n">i</span><span class="o">=</span><span class="mi">1</span><span class="p">;</span> <span class="n">i</span><span class="o">&lt;=</span><span class="n">n</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="n">ret</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="n">sum</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">;</span> <span class="k">return</span> <span class="n">ret</span><span class="p">;</span> <span class="p">}</span> <span class="kt">void</span> <span class="nf">Naive</span><span class="p">(</span><span class="kt">int</span> <span class="n">n</span><span class="p">,</span> <span class="k">const</span> <span class="n">clauses</span> <span class="o">&amp;</span><span class="n">cl</span><span class="p">){</span> <span class="n">State</span> <span class="n">state</span><span class="p">;</span> <span class="k">for</span><span class="p">(</span><span class="kt">int</span> <span class="n">i</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span> <span class="n">i</span><span class="o">&lt;</span><span class="p">(</span><span class="mi">1</span><span class="o">&lt;&lt;</span><span class="n">n</span><span class="p">);</span> <span class="n">i</span><span class="o">++</span><span class="p">){</span> <span class="k">for</span><span class="p">(</span><span class="kt">int</span> <span class="n">j</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span> <span class="n">j</span><span class="o">&lt;</span><span class="n">n</span><span class="p">;</span> <span class="n">j</span><span class="o">++</span><span class="p">)</span> <span class="n">state</span><span class="p">[</span><span class="n">j</span><span class="o">+</span><span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="p">(</span><span class="n">i</span> <span class="o">&amp;</span> <span class="p">(</span><span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="n">j</span><span class="p">))</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">;</span> <span class="k">if</span><span class="p">(</span><span class="n">state</span><span class="p">.</span><span class="n">score</span><span class="p">(</span><span class="n">cl</span><span class="p">)</span> <span class="o">==</span> <span class="n">cl</span><span class="p">.</span><span class="n">size</span><span class="p">()){</span> <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">"1</span><span class="se">\n</span><span class="s">"</span><span class="p">;</span> <span class="n">state</span><span class="p">.</span><span class="n">print</span><span class="p">(</span><span class="n">n</span><span class="p">);</span> <span class="k">return</span><span class="p">;</span> <span class="p">}</span> <span class="p">}</span> <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">"0</span><span class="se">\n</span><span class="s">"</span><span class="p">;</span> <span class="p">}</span> <span class="kt">bool</span> <span class="nf">SimulatedAnnealing</span><span class="p">(</span><span class="kt">int</span> <span class="n">n</span><span class="p">,</span> <span class="k">const</span> <span class="n">clauses</span> <span class="o">&amp;</span><span class="n">cl</span><span class="p">,</span> <span class="n">State</span> <span class="n">init</span><span class="p">,</span> <span class="kt">double</span> <span class="n">k</span><span class="p">,</span> <span class="kt">double</span> <span class="n">t</span><span class="p">,</span> <span class="kt">double</span> <span class="n">delta</span><span class="p">,</span> <span class="kt">int</span> <span class="n">epoch</span><span class="p">,</span> <span class="kt">int</span> <span class="n">select</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span> <span class="kt">int</span> <span class="n">seed</span><span class="o">=</span><span class="mh">0x917917</span><span class="p">){</span> <span class="n">State</span> <span class="n">now</span> <span class="o">=</span> <span class="n">init</span><span class="p">;</span> <span class="kt">int</span> <span class="n">score</span> <span class="o">=</span> <span class="n">now</span><span class="p">.</span><span class="n">score</span><span class="p">(</span><span class="n">cl</span><span class="p">);</span> <span class="n">Random</span> <span class="n">rnd</span><span class="p">(</span><span class="n">n</span><span class="p">,</span> <span class="n">seed</span><span class="p">);</span> <span class="k">for</span><span class="p">(</span><span class="kt">int</span> <span class="n">iter</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span> <span class="n">iter</span><span class="o">&lt;</span><span class="n">epoch</span><span class="p">;</span> <span class="n">iter</span><span class="o">++</span><span class="p">){</span> <span class="n">State</span> <span class="n">nxt</span> <span class="o">=</span> <span class="n">now</span><span class="p">;</span> <span class="k">for</span><span class="p">(</span><span class="kt">int</span> <span class="n">j</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span> <span class="n">j</span><span class="o">&lt;</span><span class="n">select</span><span class="p">;</span> <span class="n">j</span><span class="o">++</span><span class="p">)</span> <span class="n">nxt</span><span class="p">.</span><span class="n">flip</span><span class="p">(</span><span class="n">rnd</span><span class="p">);</span> <span class="kt">int</span> <span class="n">nxtScore</span> <span class="o">=</span> <span class="n">nxt</span><span class="p">.</span><span class="n">score</span><span class="p">(</span><span class="n">cl</span><span class="p">);</span> <span class="k">if</span><span class="p">(</span><span class="n">exp</span><span class="p">((</span><span class="kt">double</span><span class="p">)(</span><span class="n">nxtScore</span> <span class="o">-</span> <span class="n">score</span><span class="p">)</span> <span class="o">/</span> <span class="p">(</span><span class="n">k</span> <span class="o">*</span> <span class="n">t</span><span class="p">))</span> <span class="o">&gt;</span> <span class="n">rnd</span><span class="p">.</span><span class="n">next</span><span class="p">()){</span> <span class="n">now</span> <span class="o">=</span> <span class="n">nxt</span><span class="p">;</span> <span class="n">score</span> <span class="o">=</span> <span class="n">nxtScore</span><span class="p">;</span> <span class="p">}</span> <span class="k">if</span><span class="p">(</span><span class="n">score</span> <span class="o">==</span> <span class="n">cl</span><span class="p">.</span><span class="n">size</span><span class="p">()){</span> <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">"1</span><span class="se">\n</span><span class="s">"</span><span class="p">;</span> <span class="n">now</span><span class="p">.</span><span class="n">print</span><span class="p">(</span><span class="n">n</span><span class="p">);</span> <span class="k">return</span> <span class="nb">true</span><span class="p">;</span> <span class="p">}</span> <span class="n">t</span> <span class="o">*=</span> <span class="n">delta</span><span class="p">;</span> <span class="p">}</span> <span class="k">return</span> <span class="nb">false</span><span class="p">;</span> <span class="p">}</span> <span class="kt">bool</span> <span class="nf">RandomSearch</span><span class="p">(</span><span class="kt">int</span> <span class="n">n</span><span class="p">,</span> <span class="k">const</span> <span class="n">clauses</span> <span class="o">&amp;</span><span class="n">cl</span><span class="p">,</span> <span class="kt">int</span> <span class="n">select</span><span class="p">,</span> <span class="kt">double</span> <span class="n">tl</span><span class="p">,</span> <span class="kt">int</span> <span class="n">seed</span><span class="o">=</span><span class="mh">0x917917</span><span class="p">){</span> <span class="n">Random</span> <span class="n">rnd</span><span class="p">(</span><span class="n">seed</span><span class="p">);</span> <span class="k">while</span><span class="p">(</span><span class="n">clock</span><span class="p">()</span> <span class="o">&lt;</span> <span class="n">tl</span> <span class="o">*</span> <span class="n">CLOCKS_PER_SEC</span><span class="p">){</span> <span class="n">State</span> <span class="n">now</span> <span class="o">=</span> <span class="n">RandomInitializer</span><span class="p">(</span><span class="n">n</span><span class="p">,</span> <span class="n">rnd</span><span class="p">);</span> <span class="k">for</span><span class="p">(</span><span class="kt">int</span> <span class="n">i</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span> <span class="n">i</span><span class="o">&lt;</span><span class="n">select</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">){</span> <span class="k">if</span><span class="p">(</span><span class="n">now</span><span class="p">.</span><span class="n">flipHeuristic</span><span class="p">(</span><span class="n">rnd</span><span class="p">,</span> <span class="n">cl</span><span class="p">)){</span> <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">"1</span><span class="se">\n</span><span class="s">"</span><span class="p">;</span> <span class="n">now</span><span class="p">.</span><span class="n">print</span><span class="p">(</span><span class="n">n</span><span class="p">);</span> <span class="k">return</span> <span class="nb">true</span><span class="p">;</span> <span class="p">}</span> <span class="p">}</span> <span class="p">}</span> <span class="k">return</span> <span class="nb">false</span><span class="p">;</span> <span class="p">}</span> <span class="kt">int</span> <span class="nf">main</span><span class="p">(){</span> <span class="n">ios_base</span><span class="o">::</span><span class="n">sync_with_stdio</span><span class="p">(</span><span class="nb">false</span><span class="p">);</span> <span class="n">cin</span><span class="p">.</span><span class="n">tie</span><span class="p">(</span><span class="nb">nullptr</span><span class="p">);</span> <span class="kt">int</span> <span class="n">N</span><span class="p">,</span> <span class="n">M</span><span class="p">;</span> <span class="n">cin</span> <span class="o">&gt;&gt;</span> <span class="n">N</span> <span class="o">&gt;&gt;</span> <span class="n">M</span><span class="p">;</span> <span class="n">clauses</span> <span class="n">C</span><span class="p">(</span><span class="n">M</span><span class="p">);</span> <span class="k">for</span><span class="p">(</span><span class="k">auto</span> <span class="o">&amp;</span><span class="p">[</span><span class="n">x</span><span class="p">,</span><span class="n">y</span><span class="p">,</span><span class="n">z</span><span class="p">]</span> <span class="o">:</span> <span class="n">C</span><span class="p">)</span> <span class="n">cin</span> <span class="o">&gt;&gt;</span> <span class="n">x</span> <span class="o">&gt;&gt;</span> <span class="n">y</span> <span class="o">&gt;&gt;</span> <span class="n">z</span><span class="p">;</span> <span class="k">if</span><span class="p">(</span><span class="n">N</span> <span class="o">&lt;=</span> <span class="mi">20</span> <span class="o">&amp;&amp;</span> <span class="p">(</span><span class="n">M</span> <span class="o">&lt;&lt;</span> <span class="n">N</span><span class="p">)</span> <span class="o">&lt;=</span> <span class="mf">3e8</span><span class="p">){</span> <span class="n">Naive</span><span class="p">(</span><span class="n">N</span><span class="p">,</span> <span class="n">C</span><span class="p">);</span> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span> <span class="p">}</span> <span class="n">Random</span> <span class="n">R</span><span class="p">(</span><span class="mh">0x917917</span><span class="p">);</span> <span class="k">if</span><span class="p">(</span><span class="n">SimulatedAnnealing</span><span class="p">(</span><span class="n">N</span><span class="p">,</span> <span class="n">C</span><span class="p">,</span> <span class="n">ZeroInitializer</span><span class="p">(</span><span class="n">N</span><span class="p">)</span> <span class="p">,</span> <span class="mf">1.5</span><span class="p">,</span> <span class="mf">1.0</span><span class="p">,</span> <span class="mf">0.9999</span><span class="p">,</span> <span class="mi">50505</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mh">0x482794</span><span class="p">))</span> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span> <span class="k">if</span><span class="p">(</span><span class="n">SimulatedAnnealing</span><span class="p">(</span><span class="n">N</span><span class="p">,</span> <span class="n">C</span><span class="p">,</span> <span class="n">ZeroInitializer</span><span class="p">(</span><span class="n">N</span><span class="p">)</span> <span class="p">,</span> <span class="mf">1.5</span><span class="p">,</span> <span class="mf">1.0</span><span class="p">,</span> <span class="mf">0.9999</span><span class="p">,</span> <span class="mi">50505</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mh">0x917917</span><span class="p">))</span> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span> <span class="k">if</span><span class="p">(</span><span class="n">SimulatedAnnealing</span><span class="p">(</span><span class="n">N</span><span class="p">,</span> <span class="n">C</span><span class="p">,</span> <span class="n">ZeroInitializer</span><span class="p">(</span><span class="n">N</span><span class="p">)</span> <span class="p">,</span> <span class="mf">2.5</span><span class="p">,</span> <span class="mf">1.0</span><span class="p">,</span> <span class="mf">0.9999</span><span class="p">,</span> <span class="mi">50505</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mh">0x482794</span><span class="p">))</span> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span> <span class="k">if</span><span class="p">(</span><span class="n">SimulatedAnnealing</span><span class="p">(</span><span class="n">N</span><span class="p">,</span> <span class="n">C</span><span class="p">,</span> <span class="n">ZeroInitializer</span><span class="p">(</span><span class="n">N</span><span class="p">)</span> <span class="p">,</span> <span class="mf">1.5</span><span class="p">,</span> <span class="mf">1.0</span><span class="p">,</span> <span class="mf">0.9999</span><span class="p">,</span> <span class="mi">50505</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mh">0x917917</span><span class="p">))</span> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span> <span class="k">if</span><span class="p">(</span><span class="n">SimulatedAnnealing</span><span class="p">(</span><span class="n">N</span><span class="p">,</span> <span class="n">C</span><span class="p">,</span> <span class="n">ZeroInitializer</span><span class="p">(</span><span class="n">N</span><span class="p">)</span> <span class="p">,</span> <span class="mf">1.5</span><span class="p">,</span> <span class="mf">1.0</span><span class="p">,</span> <span class="mf">0.9995</span><span class="p">,</span> <span class="mi">50505</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mh">0x917917</span><span class="p">))</span> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span> <span class="k">if</span><span class="p">(</span><span class="n">SimulatedAnnealing</span><span class="p">(</span><span class="n">N</span><span class="p">,</span> <span class="n">C</span><span class="p">,</span> <span class="n">RandomInitializer</span><span class="p">(</span><span class="n">N</span><span class="p">,</span> <span class="n">R</span><span class="p">)</span> <span class="p">,</span> <span class="mf">1.5</span><span class="p">,</span> <span class="mf">5.0</span><span class="p">,</span> <span class="mf">0.9999</span><span class="p">,</span> <span class="mi">50505</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mh">0x917917</span><span class="p">))</span> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span> <span class="k">if</span><span class="p">(</span><span class="n">SimulatedAnnealing</span><span class="p">(</span><span class="n">N</span><span class="p">,</span> <span class="n">C</span><span class="p">,</span> <span class="n">RandomInitializer</span><span class="p">(</span><span class="n">N</span><span class="p">,</span> <span class="n">R</span><span class="p">)</span> <span class="p">,</span> <span class="mf">1.5</span><span class="p">,</span> <span class="mf">5.0</span><span class="p">,</span> <span class="mf">0.9995</span><span class="p">,</span> <span class="mi">50505</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mh">0x917917</span><span class="p">))</span> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span> <span class="k">if</span><span class="p">(</span><span class="n">SimulatedAnnealing</span><span class="p">(</span><span class="n">N</span><span class="p">,</span> <span class="n">C</span><span class="p">,</span> <span class="n">StrangeInitializer</span><span class="p">(</span><span class="n">N</span><span class="p">,</span> <span class="n">C</span><span class="p">),</span> <span class="mf">1.0</span><span class="p">,</span> <span class="mf">1.5</span><span class="p">,</span> <span class="mf">0.9999</span><span class="p">,</span> <span class="mi">50505</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mh">0x917917</span><span class="p">))</span> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span> <span class="k">if</span><span class="p">(</span><span class="n">RandomSearch</span><span class="p">(</span><span class="n">N</span><span class="p">,</span> <span class="n">C</span><span class="p">,</span> <span class="mi">100</span><span class="p">,</span> <span class="mf">1.5</span><span class="p">,</span> <span class="mh">0x814814</span><span class="p">))</span> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span> <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">"0</span><span class="se">\n</span><span class="s">"</span><span class="p">;</span> <span class="p">}</span> </code></pre></div></div>JusticeHui문제 링크 http://icpc.me/16992