APIO 2014 - Sequence

Author: Andi Qu

Intuition

After playing around with some arrays, you will probably find that the order of splits doesn't matter.

Why is this true?

Consider the case where k=2k = 2. Let the 3 arrays have sums AA, BB, and CC. If you split [A,B,C][A, B, C] into [A],[B,C][A], [B, C] and then [A],[B],[C][A], [B], [C], then you get AB+AC+BCAB + AC + BC points. If you split the array into [A,B],[C][A, B], [C] and then [A],[B],[C][A], [B], [C] though, you still get AB+AC+BCAB + AC + BC points.

A similar argument holds for k>2k > 2.

We have now greatly simplified our problem! Without loss of generality, assume we make splits from left to right.

Formulating a DP

First, let pip_i be the sum of aja_j for all jij \leq i.

Let dp[i][j]dp[i][j] be the maximum number of points we can get if we have made jj splits and the jj-th split is between aia_i and ai+1a_{i + 1}.

We have the following recurrence:

dp[i][j]=maxl=0i1(dp[l][j1]+(pipl)(pn1pi))dp[i][j] = \max_{l = 0}^{i - 1}(dp[l][j - 1] + (p_i - p_l) \cdot (p_{n - 1} - p_i))

The answer is dp[n1][k+1]dp[n - 1][k + 1] (assuming we make an extra split at the end of the array, which doesn't affect our answer).

Computing this naively would take O(n2k)\mathcal{O}(n^2k) time, but we can do much better than that.

Using CHT

Look at the DP recurrence again. Can you see how it's effectively finding the maximum of several linear functions at a point?

We can rearrange the recurrence to become

dp[i][j]=maxl=0i1(dp[l][j1]pl(pn1pi))+pi(pn1pi)dp[i][j] = \max_{l = 0}^{i - 1}(dp[l][j - 1] - p_l \cdot (p_{n - 1} - p_i)) + p_i \cdot (p_{n - 1} - p_i)

Recall that a linear function can be defined as y=mx+cy = mx + c.

In this case, we have m=plm = -p_l, x=pn1pix = p_{n - 1} - p_i, and c=dp[l][j1]c = dp[l][j - 1].

This means we can use CHT (with a deque) to compute the answer in O(nk)\mathcal{O}(nk) - a significant improvement!

One Final Optimization

Unfortunately, if we implement this solution directly, we exceed the memory limits.

Notice how dp[i][j]dp[i][j] only depends on dp[l][j1]dp[l][j - 1]. This means that instead of storing kk arrays of dp[i]dp[i], we can simply store two dpdp arrays and alternate between them.

This brings the memory used down to O(n)\mathcal{O}(n), which is good enough to pass.

Implementation

Time Complexity: O(nk)\mathcal{O}(nk)

Memory Complexity: O(n)\mathcal{O}(n)

#include <bits/stdc++.h>
#define FOR(i, x, y) for (int i = x; i < y; i++)
typedef long long ll;
using namespace std;
int n, k;
int where[201][100001];
ll pref[100001]{0}, dp[2][100001], q[100001], l = 1, r = 1;
bool case1(int x, int y, int i) {

Join the USACO Forum!

Stuck on a problem, or don't understand a module? Join the USACO Forum and get help from other competitive programmers!