Để dễ hình dung hơn, với mỗi truy vấn có các tham số $s,f,c,r$, ta sẽ hiểu là cần xây dựng một dãy $a_{p_1}, a_{p_2}, a_{p_3}, ... ,a_{p_{r+1}}$, với dãy $p$ không giảm và $p_1 = s$ và $p_{r+1} = f$, sao cho $val = max (a_{p_i}-a_{p_{i-1}})$ là bé nhất. Kết quả của truy vấn sẽ chính là $val*c$. Như vậy kết quả của bài toán sẽ là $max(val*c)$ với mọi truy vấn. # Subtask 1 - Ta sẽ dùng phương pháp chặt nhị phân kết quả để tìm giá trị $val$ . - Với mỗi giá trị $mid$, ta có thể kiểm tra xem nó có thỏa mãn không bằng cách xuất phát từ $a_s$, đi đến một vị trí $i$ sao cho $i$ lớn nhất, $i > s$ và $a_i - a_s \le mid$. Ta tính đây là một lần **dừng lại**. Sau đó, ta lại xuất phát từ $i$ và lặp lại hành động tương tự. Nếu ta có thể đi tới được $f$ mà không cần **dừng lại** quá $r$ lần, giá trị $mid$ sẽ thỏa mãn. - Như vậy, ta có thể chặt nhị phân để tìm $val$ nhỏ nhất thỏa mãn. - Độ phức tạp $O(n*log)$ cho **một** truy vấn. # Subtask 3 - Tuy lần này, $n \le 400$, nhưng $m$ lại rất lớn, lên tới $5*10^5$ nên một thuật toán như $subtask$ $1$ sẽ không thể chạy. - Ta cần sử dụng kĩ thuật quy hoạch động cho bài toán này. Gọi $dp(l,r,k)$ là kết quả tối ưu (giá trị $val$) cho đoạn $[l,r]$ nếu ta chỉ được dừng lại $k$ lần. Như vậy, với mỗi truy vấn ta sẽ lấy kết quả với $max (c*dp(s,f,r))$. - Ta có công thức quy hoạch động như sau: $dp(l,r,k) = min(max(dp(l,x,k-1),a[r]-a[x]))$, với $x \in [l,r-1]$. - Như vậy ta sẽ thu về thuật toán có độ phức tạp $O(n^4)$, chỉ đủ để qua $subtask$ $2$. - Đến đây ta cần tối ưu công thức này như sau: - Gọi $optimize(l,r,k) = x$ là vị trí tốt nhất mà $dp(l,r,k) = max (dp(l,x,k-1),a[r]-a[x])$ đạt $min$. - Ta có nhận xét rằng $optimize(l,r,k) \le optimize(l,r+1,k)$. - Mặt khác, ta có $dp(l,r,k) = min (max (dp(l,x,k-1),a[r]-a[x]))$, mà $dp(l,x,k-1) \le dp (l,x+1,k-1)$, đồng thời $a[r] - a[x] > a[r] - a[x+1]$ và hàm $y = max (dp(l,x,k-1),a[r]-a[x])$ sẽ có dạng giảm dần xuống đáy với từng giá trị $x$ rồi sau khi chạm đáy giá trị sẽ tăng dần. Như vậy, ta chỉ cần tăng $x$ tới khi nào $max (dp(l,x,k-1),a[r]-a[x])$ chạm đáy. - Vậy ta có thể thực hiện thuật toán $2$ con trỏ để tính được mảng $optimize$ song song với việc tính mảng $dp$. - Từ đó ta thu được thuật toán $O(n^3)$. - Tuy vậy, ta cần tối ưu hóa bộ nhớ, dễ thấy ta không cần lưu chiều $l$ của $dp(l,r,k)$, đồng thời không cần lưu mảng $optimize$. Từ đó bộ nhớ giảm xuống còn $N^2$. Code: ```cpp #include<iostream> #include<vector> using namespace std; const int N = 405; const int M = 1e5+5; int d[N][N]; int n,m; int a[M]; long long res = 0; struct qr { int f,c,r; }; vector<qr> v[N]; namespace sub1 { int s,f,c,r; bool check (int mid) { int res = 0; int st = s; for (int i=s; i<=f; i++) { int j = i; while (j < f && a[j+1]-a[i] <= mid) { j++; } if (j == i) return false; i = j-1; res++; if (j == f) break; } res--; return res <= r; } void solve () { for (int i=1; i<=n; i++) cin >> a[i]; cin >> s >> f >> c >> r; long long lef = 0, rig = 1e9, res = 1e9; while (rig >= lef) { int mid = (rig+lef) >> 1; if (check(mid)) res = mid, rig = mid - 1; else lef = mid + 1; } cout << 1ll*res*c << '\n'; } } void cal () { for (int l=1; l<=n; l++) { for (int r=l; r<=n; r++) { d[r][0] = a[r] - a[l]; } for (int k=1; k<=n; k++) { int opt = l; for (int r=l; r<=n; r++) { while (opt < r && max (d[opt][k-1],a[r]-a[opt]) >= max (d[opt+1][k-1],a[r]-a[opt+1])) { opt++; } d[r][k] = max (d[opt][k-1],a[r]-a[opt]); } } for (auto val : v[l]) { int f = val.f, c = val.c, r = val.r; res = max (res,1ll*d[f][r]*c); } } } signed main() { ios_base::sync_with_stdio(false); cin.tie(0); cin >> n >> m; if (m == 1) { sub1::solve(); return 0; } for (int i=1; i<=n; i++) { cin >> a[i]; } for (int i=1; i<=m; i++) { int s,f,c,r; cin >> s >> f >> c >> r; v[s].push_back({f,c,r}); } cal(); cout << res; } ```