AtCoder Beginner Contest 240 F - Sum Sum Max

原题链接F - Sum Sum Max

首先令\(z_i = \sum\limits_{k = 1}^i y_k\)\(z_0 = 0\)\(z_i\)就是第\(i\)段相同的个数的前缀和.

对于第\(i\)段和\(i - 1\)段,我们有\(z_{i - 1}\leq k \leq z_i\),这一段的值就全是\(x_i\),相当于\(C_k = x_i\).

对于某一段\(1 \leq n \leq z_i - z_{i - 1}\),有\(n\)\(x_i\)

\[B_{z_i - 1 + n} = B_{z_i - 1} + n * x_i \]

\[\begin{equation*} \begin{aligned} A_{z_i - 1 + n} &= A_{z_i - 1} + \sum\limits_{k = 1}^nB_{z_i - 1 + k} \\ &= A_{z_i - 1} + \sum\limits_{k = 1}^n( B_{z_i - 1} + K * x_i) \\ &= A_{z_i - 1} + B_{z_i - 1}n + x_i \times \cfrac{n(n + 1)}{2} \end{aligned} \end{equation*} \]

\(A_{z_i - 1 + n} = a * n^2 + bn + c\)看成一个二次函数\(f(n)\),所以我们的任务就是对于\(1 \leq n \leq z_i - z_{i - 1}\),求\(f\)的最值.

如果\(a > 0\),那么最大值在两端点\(f(1)\)\(f(z_i - z_{i - 1})\)处取得。
如果\(a < 0\),那么就是单峰函数,我们可以使用三分,令\(ml = (l + r) / 2, mr = ml + 1\),结束条件\(r - l > 2\),那么最终结束\(r - l == 2\),所以我们的最值就是\(f(l + r)\)或者\(f(r - 1)\)

#include <bits/stdc++.h>

using namespace std;

using ll = long long;

template<typename F> ll ternary_search(ll l, ll r, const F& f) {
    while (r - l > 2) {
        const ll ml = (l + r) / 2;
        const ll mr = ml + 1;
        if (f(ml) < f(mr)) {
            l = ml;
        } else {
            r = mr;
        }
    }
    
    return f(l + 1);
}

void solve() {
    int n, m;
    cin >> n >> m;
    ll res = numeric_limits<ll>::min();
    ll a = 0, b = 0;
    for (int i = 1; i <= n; i++) {
        ll x, y; cin >> x >> y;
        const auto f = [&](const ll k) {
            return a + b * k + k * (k + 1) / 2 * x;
        };

        if (x > 0) {
            res = max({res, f(1), f(y)});
        } else {
            res = max(res, ternary_search(0, y + 1, f));
        }
        a = f(y);
        b += x * y;
    }

    cout << res << "\n";
}

int main() {
    ios::sync_with_stdio(false); cin.tie(0);

    int test;
    cin >> test;
    for (int i = 0; i < test; ++i) solve();

    return 0;
}
posted @ 2022-02-21 20:38  Xxaj5  阅读(143)  评论(0)    收藏  举报