---
# System prepended metadata

title: Free Contest Testing Round 20 - BALLOON

---

Free Contest Testing Round 20 - BALLOON
===

[Link](https://oj.vnoi.info/problem/fct020_balloon)

-----

### Hướng dẫn

Gọi $f[i][c]$ là tổng điểm số cao nhất xét đến $[i]$ và đã lấy **đúng** $[c]$ phần tử có vị trí không cách nhau quá $M$ đơn vị

Hiển nhiên $f[i][1] = a[i]$

Từ đề, ta có $f[i][c] = max(f[j][c - 1]\ |\ max(1, i - d) \leq j \leq i) + c \times a_i$

Trong trường hợp không tồn tại số $j$ thỏa mãn thì $f[i][c] = -\infty$

Đê tìm max trên một đoạn cách nhau không quá $M$ đơn vị, ta có nhiều cách
- Segment Tree: $O(n)$ dựng - $O(log\ n)$ truy vấn
- Sprase Table: $O(n\ log\ n)$ dựng - $O(1)$ truy vấn
- SQRT Tree: $O(n\ log\ log\ n)$ dựng - $O(1)$ truy vấn
- Sliding Window Deque // Cartesian Tree: $O(1)$ cập nhật - $O(1)$ truy vấn

Nhận thấy rằng $f[i][c]$ chỉ phụ thuộc vào $f[j][c - 1]$ nên ta có thể giảm chiều $O(n \times k) \rightarrow O(n \times 2)$

-----

### Code 

> **Time:** $O(n \times k)$
> **Space:** $O(n)$ 
> **Algo:** DP, Sliding Window Deque, Space Optimized DP

```cpp=
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <deque>

using namespace std;

typedef long long ll;

const int LIM = 200200;
const int INF = 0x3f3f3f3f;
const ll LINF = 0x3f3f3f3f3f3f3f3f;

template<typename T> void maximize(T &res, const T &val) { if (res < val) res = val; }
template<typename T> void minimize(T &res, const T &val) { if (res > val) res = val; }

int n, d, k;
int a[LIM];
ll f[LIM][2];
int main()
{
    ios::sync_with_stdio(NULL);
    cin.tie(NULL);
    
    cin >> n >> d >> k;
    for (int i = 1; i <= n; ++i)
        cin >> a[i];
    
    ll res = *max_element(a + 1, a + n + 1);
    memset(f, 0, sizeof(f));

    for (int i = 1; i <= n; ++i) f[i][1] = a[i];
    for (int t = 2; t <= k; ++t)
    {
        bool cur = t & 1;
        bool pre = !cur;

        deque<int> S;
        for (int i = 1; i <= n; ++i)
        {
            while (S.size() && S.front() < i - d) S.pop_front();
            ll v = (S.empty()) ? -LINF : f[S.front()][pre];
            f[i][cur] = v + 1LL * t * a[i];
            maximize(res, f[i][cur]);
            while (S.size() && f[S.back()][pre] <= f[i][pre]) S.pop_back();
            S.push_back(i);
        }
    }

    cout << res;
    return 0;
}
```