문제
풀이
구간 $[1, n]$을 $k + 1$개의 구간으로 나누었을 때 $i$번째 구간의 합을 $s_i$라 합시다. 이때 $i - 1$번째 구간과 $i$번째 구간 사이를 나누는 경우 점수에는 $s_1s_i + s_1s_{i + 1} + s_1s_{i + 2} + \dots + s_2s_i + s_2s_{i + 1} + \dots$가 더해지는데, 이는 $[1, i - 1]$에서 하나를 뽑고 $[i, k + 1]$에서 하나를 뽑아 $s$를 곱한 값을 모두 더한 것과 같습니다. 따라서 $k$번 나누고 나서 얻은 점수는 결국 모든 $k + 1$개에서 두개를 뽑아 서로 곱한 값을 모두 더한 것과 같게 됩니다.
따라서 어떤 순서로 나누든 결과가 같다면 점수도 같음을 알 수 있습니다.
$dp[t][i]$를 구간 $[1, i]$를 $t + 1$개의 구간으로 나눌 때 얻을 수 있는 최대의 점수로 정의합시다. 그렇다면 아래의 점화식으로 $dp$를 구할 수 있습니다.
\[dp[t][i] = \max(dp[t - 1][j - 1] + (S_i - S_{j - 1})S_{j - 1})\]메모리 제한이 그렇게 널널하지 않으므로 아래 두 풀이 모두 토글링 기법을 사용해서 메모리를 절약했습니다.
DnC
$S_{j - 1}$을 왼쪽 구간의 합, $(S_i - S_{j - 1})$을 오른쪽 구간의 합이라 부르겠습니다.
$dp[t][i] = dp[t - 1][j - 1] + (S_i - S_{j - 1})S_{j - 1}$을 만족하는 $j$를 $opt$라 합시다.
$t$가 같다면 $i$가 커질수록 $dp[t][i]$의 값도 증가합니다.
두 수의 합이 일정할 때 두 수의 곱은 두 수의 차가 작을수록 커집니다.
따라서 $opt$는 왼쪽 구간의 합과 오른쪽 구간의 합의 차가 가장 작도록 하는 $j$보다 크거나 같아야 합니다. 그렇지 않은 경우 $opt$가 증가하면 $dp$값과 두 구간의 곱 모두 증가하므로 $opt$가 최적이 아님을 알 수 있습니다.
$i$를 증가시켜 봅시다. 값은 그대로인데 두 구간의 곱이 증가합니다. 이때 값이 늘어나는 구간은 오른쪽 구간이므로 왼쪽 구간의 합이 클 수록 두 구간의 곱이 더 많이 커집니다. 따라서 $opt$는 증가합니다.
$i$를 감소시켜 봅시다. $dp$값은 그대로인데 두 구간의 곱이 감소합니다. 아까와 마찬가지로 왼쪽 구간의 합이 작을수록 두 구간의 곱도 덜 작아집니다. 따라서 $opt$는 감소합니다.
따라서 $i < i’$인 경우 $opt_i \le opt_i’$이므로 분할 정복을 사용한 최적화를 통해 시간 복잡도 $\mathcal{O}(kn\log n)$에 해결할 수 있습니다.
코드
1 | |
CHT
점화식을 약간 변형하면 $dp[t][i] = \max(S_{j - 1}S_i - S_{j - 1}^2 + dp[t - 1][j - 1])$ 이 됩니다. 이는 변수가 $S_i$이고 기울기가 $S_{j - 1}$, y 절편이 $-S_{j - 1}^2 + dp[t - 1][j - 1]$인 직선으로 볼 수 있습니다.
기울기와 변수 모두 단조증가하므로 단조 큐를 이용한 볼록 껍질을 이용한 최적화로 시간 복잡도 $\mathcal{O}(kn)$에 해결할 수 있습니다.
점화식에서 $j \le i$ 이어야 하므로 $i \le n - (k - t)$인 $i$에 대해서만 계산해서 역추적 시에 항상 더 작은 인덱스가 나오도록 하면 됩니다.
코드
1 | |