Hướng dẫn giải của Bedao OI Contest 6 - Lại là một bài toán cực đại hoá


Chỉ dùng lời giải này khi không có ý tưởng, và đừng copy-paste code từ lời giải này. Hãy tôn trọng người ra đề và người viết lời giải.
Nộp một lời giải chính thức trước khi tự giải là một hành động có thể bị ban.

Với ~N \leq 1000~:

  • Gọi ~dp[k][i]~ là tổng điểm lớn nhất đạt được khi xét tới phần tử thứ ~i~ cắt được ~k~ đoạn.

  • Ta có công thức quy hoạch động: $$dp[k][i] = max_{j=1}^{i-1}(dp[k-1][j] + max_{t=j}^i a_t + min_{t=j}^i a_t)$$

Với ~N \leq 10^5~:

  • Ta cần tối ưu lần duyệt ~j~ ở công thức quy hoạch động trên.

  • Khi xét tới phần tử thứ ~i~:

    • ~dmax[j]~ là lượng thay đổi khi đi từ phần tử ~j + 1~ về ~j~ của trạng thái cực đại hóa.

    • ~dmin[j]~ là lượng thay đổi khi đi từ phần tử ~j + 1~ về ~j~ của trạng thái cực tiểu hóa.

Ví dụ:

Cho ~a = [3, 5, 2, 3, 4]~

  • Khi xét tới phần tử thứ 2:

    • ~dmax = [0, 0, 0, 0, 0]~, ~dmin = [-2, 0, 0, 0, 0]~
  • Khi xét tới phần tử thứ 3:

    • ~dmax = [0, 3, 0, 0, 0]~, ~dmin = [0, 0, 0, 0, 0]~
  • Khi xét tới phần tử thứ 4:

    • ~dmax = [0, 3, 0, 0, 0]~, ~dmin = [0, 0, -1, 0, 0]~
  • Khi xét tới phần tử thứ 5:

    • ~dmax = [0, 1, 0, 0, 0]~, ~dmin = [0, 0, -1, -1, 0]~

Công thức lúc này sẽ là: $$dp[k][i] = max_{j=1}^{i-1}(dp[k-1][j] + \sum_{t = j}^i dmax_t + \sum_{t = j}^i dmin_t)$$

Để xác định nhanh mảng ~dmax~, ~dmin~ cũng như truy xuất nhanh vị trí để đạt giá trị lớn nhất, ta sẽ sử dụng segment tree.

#include <bits/stdc++.h> // QioCas

using namespace std; 
using ll = long long;

#ifdef LOCAL
#include </mnt/d/Cp/Lib/debug.h>
#else
#define print(...) void(000) /* ignore */
#define debug(...) void(000) /* ignore */
#endif

const int N = 200200;

int n, k, a[N];
ll dp[21][N];

ll val[N][3];

int ceil_pow2(int n) {
    int x = 0;
    while ((1U << x) < (unsigned int)(n)) x++;
    return x;
}

int logn = 0, sz = 0;
struct Node { ll suffix = -1e18, sum = 0; } IT[500500];

Node operator + (Node u, Node v) {
    return Node{max(v.suffix, v.sum + u.suffix), u.sum + v.sum};
}

void build() {
    logn = ceil_pow2(n);
    sz = 1 << logn;

    for(int i = 1; i <= n; ++i) {
        val[i][0] = -1e18;
        val[i][1] = val[i][2] = 0;
        IT[sz + i - 1] = Node{val[i][0], 0};
    }
    for(int i = sz - 1; i > 0; --i) {
        IT[i] = IT[i << 1] + IT[i << 1 | 1];
    }
}

void update(int u, int type, ll x) {
    val[u][type] = x;
    IT[sz + u - 1] = {val[u][0] + val[u][1] + val[u][2], val[u][1] + val[u][2]};
    for(u += sz - 1; (u >>= 1) > 0; ) {
        IT[u] = IT[u << 1] + IT[u << 1 | 1];
    }
}

Node prod(int l, int r) {
    Node resL = Node{}, resR = Node{};
    for(l += sz - 1, r += sz; l < r; l >>= 1, r >>= 1) {
        if(l & 1) resL = resL + IT[l++];
        if(r & 1) resR = IT[--r] + resR;
    }
    return resL + resR;
}

signed main() {
    freopen("maximize.inp", "r", stdin);
    freopen("maximize.out", "w", stdout);

    cin.tie(NULL)->sync_with_stdio(false);
    debug(file());

    cin >> n >> k;
    for(int i = 1; i <= n; ++i) {
        cin >> a[i];
    }

    fill(&dp[0][0], &dp[21 - 1][N - 1] + 1, -1e18);

    dp[0][0] = 0;

    for(int x = 1; x <= k; ++x) {
        stack<int> minq, maxq;
        minq.push(0); maxq.push(0);
        build();
        for(int i = 1; i <= n; ++i) {
            update(i, 0, dp[x - 1][i - 1]);

            while(maxq.size() > 1 && a[maxq.top()] <= a[i]) {
                update(maxq.top(), 1, 0);
                maxq.pop();
            }

            while(minq.size() > 1 && a[minq.top()] >= a[i]) {
                update(minq.top(), 2, 0);
                minq.pop();
            }
            if(maxq.top() != 0) update(maxq.top(), 1, a[maxq.top()] - a[i]);
            if(minq.top() != 0) update(minq.top(), 2, a[minq.top()] - a[i]);
            dp[x][i] = prod(1, n).suffix + 2ll * a[i];

            minq.push(i);
            maxq.push(i);
        }
    }
    cout << dp[k][n];
}

Bình luận

Hãy đọc nội quy trước khi bình luận.


Không có bình luận tại thời điểm này.