Submission #420202

#TimeUsernameProblemLanguageResultExecution timeMemory
420202rama_pangFinancial Report (JOI21_financial)C++17
100 / 100
2166 ms49520 KiB
#include <bits/stdc++.h> using namespace std; struct Segment { // max 0-segment bool allZero = false; int lft = 0; int rgt = 0; int opt = 0; Segment(int c = 0) { if (c == 0) { allZero = 1; lft = 1; rgt = 1; opt = 1; } else { allZero = 0; lft = 0; rgt = 0; opt = 0; } } friend Segment operator+(const Segment &a, const Segment &b) { Segment c(0); c.allZero = a.allZero && b.allZero; c.lft = a.lft + (a.allZero ? b.lft : 0); c.rgt = b.rgt + (b.allZero ? a.rgt : 0); c.opt = max({a.opt, b.opt, a.rgt + b.lft}); return c; } }; class MaxSubsegment { public: int sz; vector<Segment> tree; MaxSubsegment(int sz) : sz(sz) { tree.resize(2 * sz); for (int i = sz - 1; i >= 0; i--) { tree[i] = tree[i * 2] + tree[i * 2 + 1]; } } void Modify(int p, int x) { tree[p += sz] = Segment(x); for (p /= 2; p > 0; p /= 2) { tree[p] = tree[p * 2] + tree[p * 2 + 1]; } } Segment Query(int l, int r) { Segment lft(1), rgt(1); for (l += sz, r += sz + 1; l < r; l /= 2, r /= 2) { if (l & 1) lft = lft + tree[l++]; if (r & 1) rgt = tree[--r] + rgt; } return lft + rgt; } }; class SegTree { public: int sz; vector<int> tree; SegTree(int sz) : sz(sz), tree(2 * sz, -1e9) {} int Query(int l, int r) { int z = 0; for (l += sz, r += sz + 1; l < r; l /= 2, r /= 2) { if (l & 1) z = max(z, tree[l++]); if (r & 1) z = max(z, tree[--r]); } return z; } void Modify(int p, int x) { tree[p += sz] = x; for (p /= 2; p > 0; p /= 2) { tree[p] = max(tree[p * 2], tree[p * 2 + 1]); } } }; int main() { ios::sync_with_stdio(0); cin.tie(0); int N, D; cin >> N >> D; vector<int> A(N); for (int i = 0; i < N; i++) { cin >> A[i]; } { // Coordinate Compression vector<int> coords = A; sort(begin(coords), end(coords)); coords.resize(unique(begin(coords), end(coords)) - begin(coords)); for (auto &a : A) { a = lower_bound(begin(coords), end(coords), a) - begin(coords); } } // dp[pos][max_element] // find maximum value on dp[pos-D...pos][0...A[pos] - 1] -> dp[pos][A[pos]] = 1 + best // for x >= A[pos]: dp[pos-D...pos][x] -> dp[pos][x] // // If we group all occurrences of value val which has distance <= D, then // dp[i][val] <= dp[j][val] for i < j (since we can retake "val"). // We can transform the sequence such that this condition is fullfilled without // the distance <= D condition. // // How? We scan from least valued element, then when we arrive at "val", group them // which has distance <= D. Afterwards, we reassign values s.t. the ones in the back // has lower value than the ones in the front. // // After this transformation, dp[i][val] <= dp[j][val] for i < j. So, we only need // to store last occurrence of dp[i][val]. This yields O(N^2). // // For a value "val", since all adjacent occurrence has distance <= D, then the value // "val" is valid only for some subsegment of the array. We can precompute this, then // we only need to consider "active" values when transitioning. Afterwards, we don't // need the array last[], and it's basically a simple DP optimized with segment tree. // // Time complexity: O(N log N). { // Transformation s.t. dp[i][val] <= dp[j][val] for i < j int value = 0; vector<vector<int>> occ(N); for (int i = 0; i < N; i++) { occ[A[i]].emplace_back(i); } for (int v = 0; v < N; v++) { int cnt = 0; for (int i = 0; i < int(occ[v].size()); i++) { int j = i; while (j + 1 < int(occ[v].size()) && occ[v][j + 1] - occ[v][j] <= D) { j += 1; } cnt += 1; i = j; } value += cnt; cnt = 0; for (int i = 0; i < int(occ[v].size()); i++) { int j = i; while (j + 1 < int(occ[v].size()) && occ[v][j + 1] - occ[v][j] <= D) { j += 1; } cnt += 1; for (int k = i; k <= j; k++) { A[occ[v][k]] = value - cnt; } i = j; } } assert(*max_element(begin(A), end(A)) < N); } const auto DP_N2_Old = [&](int N, int D, vector<int> A) { vector<int> dp(N, -1e9); vector<int> last(N, -1e9); for (int pos = 0; pos < N; pos++) { for (int i = A[pos]; i < N; i++) { if (pos - last[i] <= D) { last[i] = pos; } } last[A[pos]] = pos; dp[A[pos]] = max(dp[A[pos]], 1); for (int i = 0; i < A[pos]; i++) { if (pos - last[i] <= D) { dp[A[pos]] = max(dp[A[pos]], dp[i] + 1); } } } int ans = 0; for (int i = A[N - 1]; i < N; i++) { if (N - 1 - last[i] <= D) { ans = max(ans, dp[i]); } } return ans; }; const auto Solve = [&](int N, int D, vector<int> A) { vector<int> startValid(N, -1); vector<int> endValid(N, -1); for (int i = 0; i < N; i++) { if (startValid[A[i]] == -1) { startValid[A[i]] = i; } endValid[A[i]] = i; } vector<vector<int>> occ(N); for (int i = 0; i < N; i++) { occ[A[i]].emplace_back(i); } MaxSubsegment maxSeg(N); for (int i = 0; i < N; i++) if (endValid[i] != -1) { for (auto j : occ[i]) { maxSeg.Modify(j, 1); } int lo = endValid[i]; int hi = N - 1; while (lo < hi) { int md = (lo + hi) / 2; if (maxSeg.Query(endValid[i], md).opt >= D) { hi = md; } else { lo = md + 1; } } endValid[i] = lo; } SegTree dpSeg(N); vector<vector<int>> endEvent(N + 1); for (int i = 0; i < N; i++) { endEvent[endValid[i] + 1].emplace_back(i); } for (int pos = 0; pos < N; pos++) { for (auto j : endEvent[pos]) { dpSeg.Modify(j, -1e9); } int val = max({dpSeg.Query(A[pos], A[pos]), 1, 1 + dpSeg.Query(0, A[pos] - 1)}); dpSeg.Modify(A[pos], val); } return dpSeg.Query(0, N - 1); }; cout << Solve(N, D, A) << '\n'; return 0; }

Compilation message (stderr)

Main.cpp: In function 'int main()':
Main.cpp:162:14: warning: variable 'DP_N2_Old' set but not used [-Wunused-but-set-variable]
  162 |   const auto DP_N2_Old = [&](int N, int D, vector<int> A) {
      |              ^~~~~~~~~
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...