서론
문제 지문과 공식 풀이는 NYPC 아카이브에서 확인할 수 있습니다. 채점은 BIKO에서 받을 수 있습니다.
제가 생각하는 난이도(solved.ac 기준)는 다음과 같습니다. 1214 부문 3번(돌 무더기)과 5번(마방진 만들기) 문제는 사람마다 편차가 클 것 같습니다. 1519 5번(편집 거리) 문제 사실상 두 가지 문제가 합쳐진 문제이므로 따로 구분해서 매겼습니다.
- 1214 부문
- [S?] 기호
- [G5] Connexion
- [P?] 돌 무더기
- [P3] 개미
- [P?] 마방진 만들기
- 1519 부문
- [G5] Connexion
- [P3] 개미
- [D4] 물 뿌리기
- [D3] 거래
- [D2] 편집 거리 - subtask 1,2,4,5
- [R5] 편집 거리 - subtask 1,2,3
1214 2번 = 1519 1번, 1214 4번 = 1519 2번입니다.
PC 화면으로 보는 분들은 우측 사이드바를 이용해 원하는 문제로 빠르게 이동할 수 있습니다.
1214 1번. 기호
문자열을 앞에서부터 차례대로 보면서, 뺄셈 기호의 수가 덧셈 기호의 수보다 커질 때마다 뺄셈 기호 하나를 덧셈 기호로 바꾸는 전략이 성립합니다.
1 | |
1214 2번 / 1519 1번. Connexion
그래프를 적절히 구성한 뒤, BFS/DFS 등을 이용해 컴포넌트의 크기를 구하면 됩니다.
1 | |
1214 3번. 돌 무더기
가장 먼저 떠오르는 풀이는 각 무더기에 있는 돌의 개수를 인자로 하는 동적 계획법입니다. 아래와 같이 구현하면 $N = \max(a,b,c)$일 때 상태 공간의 크기가 $O(N^3)$이고, 각 상태의 답을 $O(N)$에 구하는 $O(N^4)$ 풀이를 얻을 수 있습니다.
1 | |
위 풀이를 이용해 작은 범위에서 답을 구한 뒤, $D(a, b, c) = 0$인 $(a, b, c)$ 튜플을 모두 출력해놓고 열심히 쳐다보면 규칙을 찾을 수 있습니다. $g(x)$를 $x$에 2가 곱해진 횟수라고 정의합시다. $D(a, b, c) = 0$일 필요충분조건은 $g(a) = g(b) = g(c)$임을 관찰하면 문제를 해결할 수 있습니다.
$g(a) = g(b) = g(c)$인 위치가 패배하는 위치라는 것은 다음과 같이 증명할 수 있습니다.
돌이 $x$개 있는 더미가 있고, $g(x) = t$라고 합시다. $0 \le s < t$인 모든 정수 $s$에 대해, $g(y) = g(z) = s$가 성립하도록 크기가 $x$인 더미를 크기가 $y=2^s$인 더미와 $z=x-2^s$인 더미인 나눌 수 있습니다. 반대로, $s \ge t$이면 $g(y) = g(z) = s$가 되도록 더미를 나눌 수 없습니다.
즉, 세 더미의 $g(a) = g(b) = g(c)$라면 플레이어가 어떤 행동을 하더라도 $\min(g(a), g(b), g(c))$가 감소할 수밖에 없습니다. 반대로, 세 더미의 $g$값 중 하나라도 다르다면, 플레이어는 항상 세 더미의 $g$값이 같아지도록 만들 수 있습니다. 또한, 더이상 행동을 할 수 없는 상태($a,b,c \le 1$)는 $g(a) = g(b) = g(c) = 0$입니다.
따라서 세 더미의 $g$값이 같은 상태로 게임을 시작한다면, 후공이 매번 세 더미의 $g$값이 같은 상태를 되돌려주므로 선공이 질 수밖에 없습니다.
1 | |
1214 4번 / 1519 2번. 개미
$v$를 루트로 하는 서브트리의 각 정점에 개미를 정확히 한 마리씩 배치하고 남은 개미의 수를 $S[v]$라고 정의합시다. 이는 서브트리에 속한 정점들의 $A[i] - 1$ 값을 더하면 구할 수 있고, 개미가 부족하면 $S[v]$는 음수가 됩니다.
각 간선을 따라 이동하는 개미의 수에 집중합시다. 편의상 $v$의 부모 정점을 $p(v)$라고 부를 것입니다.
만약 $S = N$이라면 모든 정점에 정확히 한 마리의 개미가 배정되어야 합니다. 따라서 $S[v] > 0$인 서브트리에서는 $v$에서 $p(v)$로 $S[v]$마리의 개미가 이동해야 합니다. 반대로, $S[v] < 0$인 서브트리에서는 $p(v)$에서 $v$로 $-S[v]$마리의 개미가 이동해야 합니다. 따라서 $S = N$이면 정답은 $\sum \vert S[v] \vert$입니다.
$S = N+1$이라면 정확히 한 정점에만 두 마리의 개미가 들어가야 합니다. 개미가 두 마리 들어가는 정점을 $x$라고 하면, 간선 $(p(v), v)$ 를 통과하는 개미의 수는 다음과 같이 계산할 수 있습니다.
- $v$가 $x$ 또는 $x$의 조상인 경우: $\vert S[v] - 1 \vert$
- $v$가 $x$의 조상이 아닌 경우: $\vert S_v \vert$
즉, 루트에서 $x$로 가는 경로 위의 간선을 통과하는 개미의 수만 바뀌고, 다른 간선들의 값은 바뀌지 않습니다. 따라서 개미 한 마리를 $x$로 보내는 비용은 $D(x) = D(p(x)) + \vert S[x] - 1 \vert - \vert S[x] \vert$을 이용해 계산할 수 있습니다. 비용을 최소화하는 것이 목적으므로, 문제의 정답은 $\sum \vert S_v \vert + \min D_v$입니다.
마지막으로 $S = N+2$인 상황을 생각합시다. 정확히 두 정점에 두 마리의 개미가 들어가야 합니다. 개미가 두 마리 들어가는 두 정점을 각각 $x, y$라고 하고, $x$와 $y$의 LCA를 $l$이라고 하면, 간선 $(p(v), v)$를 통과하는 개미의 수는 다음과 같이 계산할 수 있습니다.
- $v$가 $l$ 또는 $l$의 조상인 경우: $\vert S[v] - 2 \vert$
- $v$가 $x$ 또는 $y$, 또는 그들의 조상이면서 $l$의 자손인 경우: $\vert S[v] - 1 \vert$
- 나머지: $\vert S[v] \vert$
$f(v) = \vert S[v] - 1 \vert - \vert S[v] \vert$, $g(v) = \vert S[v] - 2 \vert - \vert S[v] \vert$라고 정의하고, $D_f(x)$를 루트부터 $x$까지 $f$의 합, $D_g(x)$를 루트부터 $x$까지 $g$의 합이라고 합시다. 개미가 두 마리 들어갈 정점을 각각 $x, y$라고 하면, 비용은 $\sum \vert S[v] \vert$에서 $D_f(x) + D_f(y) - 2D_f(l) + D_g(l)$ 만큼 증가합니다. 따라서 이 식의 결과로 가능한 최솟값을 구하면 문제를 해결할 수 있습니다. 이는 트리 DP를 이용해 트리의 지름을 구하는 것처럼, 두 정점의 LCA를 고정한 다음 가장 작은 두 자식 정점의 값을 가져오는 방식으로 $O(N)$ 시간에 계산할 수 있습니다.
따라서 $S = N, N+1, N+2$인 문제를 모두 $O(N)$ 시간에 해결할 수 있습니다.
1 | |
1214 5번. 마방진 만들기
$N \le 3$일 때는 가능한 $O(N^2!)$가지 조합을 시도하면 되므로 $N = 4$인 경우만 생각해도 충분합니다.
다항 시간에 풀 수 없어보이므로 가지치기를 동반한 완전 탐색을 시도해야 할 텐데, 잘 생각해 보면 탐색하지 않고도 값을 확정지을 수 있는 칸이 존재한다는 것을 알 수 있습니다. 구체적으로,
- $(0, 0)$, $(0, 1)$, $(0, 2)$의 값이 정해지면 $(0, 3)$의 값은 자동으로 확정됩니다.
- $(1, 0)$, $(1, 1)$, $(1, 2)$의 값이 정해지면 $(1, 3)$의 값은 자동으로 확정됩니다.
- $(2, 1)$, $(2, 2)$의 값이 정해지면 나머지 6칸의 값이 자동으로 확정됩니다.
따라서 16개의 칸 중 8칸의 값만 정하면 다른 8칸의 값은 탐색할 필요가 없습니다. 따라서 탐색해야 하는 경우의 수를 $16!/8! = 518\,918\,400$ 으로 줄일 수 있고, 적절한 가지치기를 동반해 제한 시간 안에 문제를 해결할 수 있습니다.
1 | |
1519 3번. 물 뿌리기
트리 위에 원을 2개 그린 뒤, 두 원의 교집합에 속한 정점의 값을 업데이트하는 쿼리를 처리해야 합니다. 트리에서 원의 교집합이 어떤 형태인지를 먼저 관찰해 봅시다.
두 원 $C_1(v_1, r_1)$, $C_2(v_2, r_2)$가 있다고 합시다. 만약 $v_1$과 $v_2$ 사이의 거리 $d$가 $r_1+r_2$보다 크다면 교집합은 공집합입니다. 그렇지 않은 경우, 두 원의 교집합은 $v_1$에서 $v_2$ 방향으로 $x = \frac{r_1+(d-r_2)}{2}$ 만큼 이동한 지점이 중심이고, 반지름이 $r_2 - x$인 원이 됨을 알 수 있습니다. 즉, 정점을 원의 중심으로 하는 두 원의 교집합은, 정점 또는 간선의 중앙을 중심으로 하는 원입니다.
따라서 간선의 중앙에 정점을 하나씩 더 끼워넣은 크기가 $2N-1$인 트리를 만들면, 어떤 정점 $v$와의 거리가 $d$ 이하인 모든 정점의 값을 업데이트하는 문제로 바꿀 수 있습니다. 센트로이드 트리를 이용하면 사용하는 자료구조에 따라 매번 $O(\log N)$ 또는 $O(\log^2 N)$ 시간에 업데이트할 수 있습니다.
이후 단계는 단순한 다익스트라 알고리즘으로, $O(N \log N)$ 시간에 완료할 수 있습니다. 따라서 전체 시간 복잡도는 $O((N+M) \log N)$ 또는 $O(N \log N + M \log^2 N)$이며, 두 풀이 모두 제한 시간 안에 문제를 해결할 수 있습니다.
1 | |
1519 4번. 거래
서브태스크 2. $P_i \ne 0$
이득을 최대화하기 위해서는 $i$번째 물건을 산 다음, $i, i+1, \cdots, N$번째 날 중 물건이 가장 비쌀 때 팔면 됩니다. 따라서 $M_i = \max_{i \le k \le N} P_k$라고 정의하면, 문제의 답은 $\mathcal{F}(P) = \sum M_i - P_i$가 됩니다.
1 | |
하지만 다르게도 생각해 볼 수 있습니다.
어떤 정수 $h$에 대해, 가격이 $h$ 이하일 때 구매한 뒤에 $h$ 초과일 때 판매해서 수익을 얻는 횟수를 생각해 봅시다. 구체적으로, $1 \le P_i \le h$이면서 $i < j$ 이고 $P_j > h$인 $j$가 존재하는 $i$의 개수를 $f_h(P)$라고 정의하면, $\mathcal{F}(P) = \sum_{h=1}^{N-1} f_h(P)$가 성립합니다.
$f_h(P)$는 $h$ 초과가 등장하는 마지막 위치보다 앞에 있는 $1 \le P_i \le h$인 개수라고 생각할 수 있습니다. 계산하기 쉽게 만들기 위해 $g_h(P) :=$ $h$ 초과가 등장하는 마지막 인덱스보다 뒤에 있는 $1 \le P_i \le h$ 의 개수를 정의합시다. $f_h(P) = h - g_h(P)$가 성립하며, $g_h(P)$는 $h$가 증가하는 순서대로 보면 펜윅 트리 등을 이용해 $g_1(P), g_2(P), \cdots, g_{N-1}(P)$를 모두 $O(N \log N)$ 시간에 구할 수 있습니다.
따라서 $\mathcal{F}(P) = \sum_{h=1}^{N-1} f_h(P) = \sum_{h=1}^{N-1} h - g_h(P) = N(N-1)/2 - \sum_{h=1}^{N-1} g_h(P)$도 $O(N \log N)$ 시간에 구할 수 있습니다.
1 | |
아래와 같이 펜윅 트리 없이 $O(N^2)$에 계산할 수도 있습니다. 뒤에서는 이 방법을 발전시켜서 전체 문제를 해결할 것입니다.
1 | |
서브태스크 7 (100점)
설명의 편의를 위해 수열 전체에 있는 물음표의 개수를 $K$라고 합시다.
$\sum_P \mathcal{F}(P) = \sum_{h=1}^{N-1} \sum_P h - g_h(P)$ 를 계산해야 합니다. 식을 정리하면 $\sum_{h=1}^{N-1} K!h - \sum_P g_h(P) = K!N(N-1)/2 - \sum_{h=1}^{N-1} \sum_P g_h(P)$ 가 되므로, $\sum_{h=1}^{N-1} \sum_P g_h(P)$만 빠르게 계산하면 됩니다. 마찬가지로 $h$가 증가하는 순서대로 계산할 것입니다.
$h$와 $p$를 고정했을 때, $p$ 이후에 있는 모든 수가 $h$ 이하인 순열의 경우의 수를 세는 방법에 대해 생각해 봅시다. $p$ 이후애 있는 물음표의 개수를 $K_p$, 물음표 자리에 들어갈 수 있는($P$에 등장하지 않은) $h$ 이하인 수의 개수를 $L_h$라고 합시다.
$p$ 이후에 있는 모든 수가 $h$ 이하인 순열을 만들기 위해서는, 우선 $L_h$개의 수 중 $K_p$개를 골라서 배치한 다음, 남은 $K-K_p$개의 수를 남은 자리에 자유롭게 배치하면 됩니다. 따라서 $L_h! / (L_h - K_p)! \times (K-K_p)!$이 됨을 알 수 있습니다.
따라서 모든 $p, h$에 대해 $L_h! / (L_h - K_p)! \times (K-K_p)!$를 더해서 $\sum_{h=1}^{N-1} \sum_P g_h(P)$를 구할 수 있으며, $O(N^2)$ 시간에 계산할 수 있습니다.
1 | |
1519 5번. 편집 거리
두 가지 문제가 합쳐진 문제입니다.
- $N \le 100\,000$, $M \le 1\,000$, $K \le 20$ (서브태스크 1, 2, 4, 5 / 65점)
- $N \le 5\,000$, $M \le 1\,000$, $K \le 1\,000$ (서브태스크 1, 2, 3 / 55점)
첫 번째 문제는 $K$가 작다는 점을 이용해 $O(NK^2)$ 시간에 푸는 문제, 두 번째 문제는 $O(NM)$ 시간에 푸는 문제입니다.
서브태스크 1. $N \le 1000, M \le 100, K \le 100$ (8점)
$T$의 모든 접미사와 $P$의 edit distance table(D table)를 구하면 문제를 해결할 수 있습니다. D table을 한 번 구하는 데 $O(NM)$ 만큼 걸리므로 $O(N^2M)$ 시간에 문제를 해결할 수 있습니다.
1 | |
$K$ 범위 제한을 이용하면, $S$의 접미사를 볼 때 첫 $M+K$글자만 봐도 된다는 사실을 알 수 있습니다. 이를 이용하면 편집 거리가 $K$ 이하인 D table의 일부를 구하는 데 $O(M(M+K))$ 시간이 걸리므로, $O(NM^2)$ 시간에 문제를 해결할 수 있습니다. 위 코드의 7번째 줄 뒤에 n = min(n, m+K); 만 추가하면 됩니다.
서브태스크 2. $N \le 3000, M \le 1000, K \le 20$ (20점)
$P$와의 편집 거리가 $K$ 이하인 문자열의 길이는 항상 $M-K$ 이상, $M+K$ 이하입니다. 따라서 D table에서 주대각선과 $K$ 이하 만큼 떨어진 칸들만 계산해도 문제를 해결할 수 있습니다. 이 경우 D table을 한 번 구하는 데 $O(MK)$ 시간이 걸리므로, 전체 시간 복잡도는 $O(NMK)$가 됩니다.
1 | |
서브태스크 4 & 5. $N \le 100\,000, M \le 1000, K \le 20$ (65점)
D table에서 오른쪽 아래로 향하는 대각선을 보면, 항상 값이 단조증가한다는 것을 알 수 있습니다. 더 나아가서, 대각선 위에서 인접한 두 칸은 항상 0 또는 1 만큼 차이납니다. 따라서, 각 대각선마다 값이 $k$에서 $k+1$로 바뀌는 지점을 빠르게 찾을 수 있다면, 그러한 연산을 $O(NK^2)$번 수행해서 $K$ 이하인 모든 D table의 값을 알 수 있습니다.
D table에서 대각선 방향으로 같은 값이 많이 뻗어나가는지를 구하면, 값이 1 만큼 증가하는 시점을 포착할 수 있습니다. 구체적으로, $i - j$가 $s$로 일정한 칸들 중, $D(i, j) = k-1$인 마지막 칸을 $(i_{s,k-1}, j_{s,k-1})$이라고 하면, $(i_{s,k}, j_{s,k})$는 아래 세 가지 경우를 계산해서 구할 수 있습니다.
- $(i_{s,k-1}, j_{s,k-1})$에서 값을 1 만큼 증가시켜서 대각선 방향으로 내려온 다음, $(i_{s,k-1}+1, j_{s,k-1}+1)$부터 값 증가 없이 대각선 타고 내려가기
- $(i_{s-1,k-1}, j_{s-1,k-1})$에서 값을 1 만큼 증가시켜서 한 칸 내려온 다음, $(i_{s-1,k-1}+1, j_{s-1,k-1})$부터 값 증가 없이 대각선 타고 내려가기
- $(i_{s+1,k-1}, j_{s+1,k-1})$에서 값을 1 만큼 증가시켜서 한 칸 오른쪽으로 이동한 다음, $(i_{s+1,k-1}, j_{s+1,k+1}+1)$부터 값 증가 없이 대각선 타고 내려가기
대각선을 타고 내려간다는 것을 다르게 이야기하면 $A_i = B_j$가 성립할 동안 $i$와 $j$를 계속 증가시키겠다는 뜻이고, 이는 $A[i\cdots]$와 $B[j\cdots]$의 prefix가 얼마나 많이 일치하는지, 즉 longest common prefix를 구한다는 것을 의미합니다. 따라서 $A\sharp B$의 접미사 배열와 LCP 배열을 미리 전처리해 두면, 스파스 테이블 등을 이용해 $O(1)$ 시간에 대각선을 얼마나 타고 내려갈 수 있는지를 구할 수 있습니다.
각 대각선마다 DP 값의 변화를 매번 $O(1)$ 시간에 구할 수 있으므로, 한 대각선의 DP 값을 총 $O(K)$ 시간에 구할 수 있습니다. 대각선은 $-K \le i-j \le K$ 범위에서 총 $2K+1$개를 관리해야 하므로, $O(K^2)$ 시간에 $T$의 suffix에 대한 문제를 해결할 수 있으며, 전체 문제는 $O(NK^2)$ 시간에 해결할 수 있습니다.
1 | |
서브태스크 3. $N \le 5000, M \le 1000, K \le 1000$ (100점)
$T[i\cdots]$와 $P$의 D table에서 $T[i+1\cdots]$와 $P$의 D table로 전이하는 것은, 최악의 경우 $O(NM)$개의 칸의 값이 바뀔 수 있으므로 $O(NM)$보다 빠르게 할 수 없습니다. 따라서 D table의 정보를 보존하는 또다른 테이블을 만들어서 관리해야 합니다.
풀이 준비 중…