F - Variety Split Hard Editorial by en_translator
Let \(X_i=(A_1,A_2,\ldots,A_i)\) be the maximum sum of the counts of distinct elements when split into two non-empty subarrays,
\(L_i\) be the count of distinct elements in \((A_1,A_2,\ldots,A_i)\), and
\(R_i\) be the count of distinct elements in \((A_i,A_{i+1},\ldots,A_N)\).
Then the answer is \(\max_{2 \leq i \leq N-1}(X_i+R_{i+1})\).
\(L\) and \(R\) can be found in \(O(N)\) time by scanning from the front and the bottom while updating the number of distinct elements (as explained in the editorial for problem C).
Now we consider how to find \(X\).
Let \(dp_{i,j}\) be the sum of the count of distinct elements in \((A_1,A_2,\ldots,A_j)\), and the count of distinct elements in \((A_{j+1},A_{j+2},\ldots,A_i)\).
Then, \(X_i=\max(dp_{i})\).
Also, for \(1 \leq j \leq i-1\), we have
\(dp_{i+1,j}=\left \{ \begin{array}{ll} dp_{i,j}+1 & ((A_{j+1},A_{j+2},\ldots,A_{i})\text{ does not contain }A_{i+1}) \\ dp_{i,j} & ((A_{j+1},A_{j+2},\ldots,A_{i})\text{ contains }A_{i+1}), \end{array} \right\} \)
and \(dp_{i+1,i}=L_i+1\).
Whether \((A_{j+1},A_{j+2},\ldots,A_{i})\) contains \(A_{i+1}\) is monotonic with respect to \(j\), so we have \(dp_{i+1,j}=dp_{i,j}+1\) for integers \(j\) greater than some integer \(k\), and \(dp_{i+1,j}=dp_{i,j}\) for the other integers \(j\). In other words, the transition from \(dp_{i}\) to \(dp_{i+1}\) can be represented as a segment addition.
This can be processed in the manner of inline DP where the memory is reused, using a lazy segment tree that supports segment addition and segment maximum-value retrieval.
Therefore, the problem can be solved in \(O(N\log N)\) time.
Sample code (C++)
#include <bits/stdc++.h>
#include <atcoder/lazysegtree>
using namespace std;
int op(int a, int b) { return max(a, b); }
int e() { return -1e9; }
int mapping(int f, int x) { return f + x; }
int composition(int f, int g) { return f + g; }
int id() { return 0; }
int main() {
int n;
cin >> n;
vector<int> a(n);
for (auto& e : a)
cin >> e;
vector<int> dp(n, 0);
vector<int> suml(n), sumr(n);
int now = 0;
vector<int> vis(n + 1, 0);
for (int i = 0; i < n; ++i) {
if (++vis[a[i]] == 1) {
now++;
}
suml[i] = now;
}
vis = vector<int>(n + 1, 0);
now = 0;
for (int i = n - 1; i >= 0; --i) {
if (++vis[a[i]] == 1) {
now++;
}
sumr[i] = now;
}
atcoder::lazy_segtree<int, op, e, int, mapping, composition, id> seg(n);
vector<int> last(n + 1, -1);
for (int i = 0; i < n; ++i) {
seg.apply(last[a[i]] == -1 ? 0 : last[a[i]], i, 1);
dp[i] = seg.prod(0, i);
seg.set(i, suml[i]);
last[a[i]] = i;
}
int ans = 0;
for (int i = 1; i < n - 1; ++i) {
ans = max(ans, dp[i] + sumr[i + 1]);
}
cout << ans << endl;
}
posted:
last update: