#include <bits/stdc++.h>
using namespace std;
int bitmask(int n, int d, vector<int> a) {
int ans = 0;
for (int mask = 1; mask < (1 << n); mask++) {
vector<int> active; for (int i = 0; i < n; i++) if (mask & (1 << i)) active.push_back(i);
int calc = 1, cmax = a[active[0]];
for (int i = 1; i < active.size(); i++) {
if (active[i] - active[i-1] > d) {
calc = 0; break;
}
if (a[active[i]] > cmax) {
cmax = a[active[i]];
calc++;
}
}
ans = max(ans, calc);
}
return ans;
}
// because reachability is monotone forwards, we could do range chmax for all the nodes i can reach
// could get 12 pts for N = 1 as follows: (1) use binary search to find largest array starting at i where max(arr) <= a[i]
// this gives reachable[i]
// (2) ...
struct SegTree {
int n; vector<int> st;
SegTree() {};
SegTree(int N) {
n = N; st.resize(4*n);
}
int update(int i, int l, int r, int k, int v) {
if (!(l <= k && k < r)) return st[i];
if (l + 1 == r) return st[i] = v;
int m = l + (r - l) / 2;
return st[i] = max(update(2*i+1, l, m, k, v), update(2*i+2, m, r, k, v));
}
int query(int i, int l, int r, int ql, int qr) {
if (r <= ql || qr <= l) return 0;
if (ql <= l && r <= qr) return st[i];
int m = l + (r - l) / 2;
return max(query(2*i+1, l, m, ql, qr), query(2*i+2, m, r, ql, qr));
}
void update(int k, int v) {
update(0, 0, n, k, v);
}
int query(int l, int r) {
return query(0, 0, n, l, r);
}
void trace() {
for (int i = 0; i < n; i++) cout << query(i, i+1) << " "; cout << "\n";
}
};
/*
solution so far:
(1) the problem is very similar to longest increasing subsequence, infact when D = n, it is LIS
(2) if we process indices in ascending order of a[idx], we still get the answer. ⇒ DP[i] depends only on j < i and a[j] < a[i]
(3) the set of nodes which can be reached from index i using only nodes where a[j] <= a[i] form a contiguous subarray
(4) DP[i] = max(all nodes that can reach i, and have a[j] < a[i]). slight restrictioning on (2). we can use a segment tree to do
2 things at once here. first, sort all indices by a[idx]. we can now query [0, sorted_pos[i]], and get the maximum value of all
DP[i] where j < i. the second part is we initialise and disable values when needed. so when the contiguous subarray of reachable[j]
ends, we set DP[i] to 0 inside of the segtree, this way we only query active nodes. the final step is now to efficiently find the
value of end[j] (first index j cannot reach)
(5) if D = N, end[j] = n for all j. if D = 1, we can binary search to find the first index where max(a[i:j]) is > a[i] (n log^2 n with segtree)
(6) maybe we can use a segtree to compute it for all D?
*/
int dp(int n, int d, vector<int> a) {
vector<int> indices(n); iota(indices.begin(), indices.end(), 0);
sort(indices.begin(), indices.end(), [&](int i, int j) {return a[i] == a[j] ? i > j : a[i] < a[j];});
vector<int> ridx(n); for (int i = 0; i < n; i++) ridx[indices[i]] = i;
vector<vector<int>> ends(n+2, vector<int>()), ends2(n+2, vector<int>());
vector<int> M(n, INT_MAX);
multiset<int> window;
int l = 0, r = 0;
for (; r < n; r++) {
if (r <= d) {
M[r] = INT_MIN;
window.insert(a[r]);
continue;
}
window.erase(window.find(a[l++]));
M[r] = *window.begin();
window.insert(a[r]);
}
auto rmq = SegTree(n); for (int i = 0; i < n; i++) rmq.update(i, M[i]);
for (int i = 0; i < n; i++) {
int lo = i, hi = n - 1;
while (lo < hi) {
int mid = lo + (hi - lo / 2);
bool check = rmq.query(i+1, mid+1) > a[i];
if (check) {
hi = mid;
} else {
lo = mid + 1;
}
}
ends[lo].push_back(i);
// for (int j = i+1; j < n; j++) {
// if (M[j] > a[i]) {
// ends[j].push_back(i);
// break;
// }
// }
}
// for (int i = 0; i < n; i++) {
// int max_dist = 0, prev_se = i;
// for (int j = i; j < n; j++) {
// max_dist = max(max_dist, j - prev_se);
// if (a[j] <= a[i]) prev_se = j;
// if (max_dist > d) {
// ends2[j].push_back(i);
// break;
// }
// }
// }
// for (int i = 0; i < n; i++) {
// cout << "i: " << i << "\n";
// for (auto &el : ends2[i]) cout << el << " "; cout << "\n";
// for (auto &el : ends[i]) cout << el << " "; cout << "\n";
// }
vector<int> DP(n, 0);
auto st = SegTree(n);
for (int i = 0; i < n; i++) {
for (auto &el : ends[i]) {
st.update(ridx[el], 0);
}
DP[i] = st.query(0, ridx[i]) + 1;
st.update(ridx[i], DP[i]);
}
return *max_element(DP.begin(), DP.end());
}
void debug() {
mt19937 rng(chrono::system_clock::now().time_since_epoch().count());
uniform_int_distribution dist(0, 5);
for (int TRIAL = 0; TRIAL < 1'000; TRIAL++) {
int N = 6; vector<int> INPUT; for (int i = 0; i < N; i++) INPUT.push_back(dist(rng));
for (int D = 1; D <= N; D++) {
int bm = bitmask(N, D, INPUT);
int dpa = dp(N, D, INPUT);
if (bm != dpa) {
cout << "Failure on N: " << N << " D: " << D << " and array:"; for (auto &el : INPUT) cout << " " << el; cout << "\n";
cout << "Bitmask: " << bm << " DP: " << dpa << "\n";
break;
}
}
}
}
int main() {
// debug();
cin.tie(0)->sync_with_stdio(0);
int n, d; cin >> n >> d;
vector<int> a(n); for (int i = 0; i < n; i++) cin >> a[i];
cout << dp(n, d, a) << "\n";
return 0;
}
# | Verdict | Execution time | Memory | Grader output |
---|
Fetching results... |
# | Verdict | Execution time | Memory | Grader output |
---|
Fetching results... |
# | Verdict | Execution time | Memory | Grader output |
---|
Fetching results... |
# | Verdict | Execution time | Memory | Grader output |
---|
Fetching results... |
# | Verdict | Execution time | Memory | Grader output |
---|
Fetching results... |
# | Verdict | Execution time | Memory | Grader output |
---|
Fetching results... |