AtCoder Beginner Contest 353 abc353 G - Merchant Takahashi 题解

题意

AtCoder Beginner Contest 353 abc353 G - Merchant Takahashi

\(n(1 \le n \le 2e5)\) 个城镇和 \(m(1 \le m \le 2e5)\) 个活动依次发生,第 \(i(1 \le i \le m)\) 个活动在第 \(t_i(1 \le t_i \le n)\) 个城镇举办,你如果参加则可获得 \(p_i(1 \le p_i \le 1e13)\) 的受益。你从 \(i\) 城镇移动到 \(j\) 城镇需要花费 \(c|i-j|, (1 \le c \le 1e9)\)。求最大受益。

思路

这道题能够轻松的写出 \(O(n^2m)\) 的dp写法。设 \(dp[i][j]\) 为举行了前 \(i\) 个活动,并且最终在第 \(j\) 个城镇时候的最大受益。

\[dp[i][j] = d[i-1][k] - c|j-k| + (j == t_i) \times p_i \]

这里我们发现其实枚举第 \(i\) 个活动时候,只需要考虑最终在第 \(t_i\) 个城镇就好。这时候复杂度就降为了 \(O(nm)\),方程变为了

\[dp[i][t_i] = max_{1 \le j \le n} \{ dp[i-1][j] - c|j-t_i| \} + p_i \]

优化第一维就变成了\(dp[t_i] = max_{1 \le j \le n} \{ dp[j] - c|j-t_i| \} + p_i\)
但复杂度依旧很高,继续考虑,尝试把绝对值符号给拆开,方程变为

\[dp[t_i] = max \{ max_{1 \le j \le t_i} \{ dp[j] - c \times (t_i - j) \}, max_{t_i \le j \le n} \{ dp[j] - c \times (j - t_i) \} \} + p_i \]

\[dp[t_i] = max \{ max_{1 \le j \le t_i} \{ dp[j] + cj - ct_i \}, max_{t_i \le j \le n} \{ dp[j] - cj + ct_i \} \} + p_i \]

这里问题就变为了快速获取 \([1,t_i]\)\([t_i, n]\) 中的最大值,而线段树可以完成这个事,我们可以建2个线段树来维护区间最大值。
一个树维护 \(max_{1 \le j \le n} \{ dp[j] + cj \}\)
一个树维护 \(max_{1 \le j \le n} \{ dp[j] - cj \}\)
线段树的复杂度是 \(O(logn)\),最终整体复杂度为 \(O(mlogn)\)

代码

#include <algorithm>
#include <cmath>
#include <cstring>
#include <iostream>
#include <map>
#include <queue>
#include <set>
#include <stack>
#include <string>
#include <vector>
#include <bitset>
#include <numeric>

#define fi first
#define se second
#define endl '\n'


using namespace std;
using ll = long long;
using ull = unsigned long long;
using pii = pair<int, int>;

const ll inf = 0x3f3f3f3f3f3f3f3f;
const ull P = 131;
const double eps = 1e-4;
const int N = 2e5 + 10;

// 线段树
struct SegmentTree {
    struct Node{
        int l, r;
        ll sum;
    };
    vector<Node> tr;

    void init(int n, vector<ll> &a) {
        tr.resize(n * 4 + 1);
        build(1, 1, n, a);
    }

    inline int lchild(int u) {
        return u << 1;
    }
    inline int rchild(int u) {
        return u << 1 | 1;
    }

    void pushup(int u) {
        tr[u].sum = max(tr[lchild(u)].sum, tr[rchild(u)].sum);
    }
    
    void build(int u, int l, int r, vector<ll> &a) {
        if(l == r) tr[u] = {l, r, a[l]};
        else {
            tr[u] = {l, r};
            int mid = (l + r) >> 1;
            build(lchild(u), l, mid, a), build(rchild(u), mid+1, r, a);
            pushup(u);
        }
    }

    void modify(int u, int x, ll v) {
        if(tr[u].l == tr[u].r) {
            tr[u].sum = max(tr[u].sum, v);
        }
        else {
            int mid = (tr[u].l + tr[u].r) >> 1;
            if(x <= mid) modify(lchild(u), x, v);
            else modify(rchild(u), x, v);
            pushup(u);
        }
    }

    ll query(int u, int l, int r){
        if(tr[u].l >= l && tr[u].r <= r) return tr[u].sum;
        int mid = (tr[u].l + tr[u].r) >> 1;
        ll res = -inf;
        if(l <= mid) res = max(res, query(lchild(u), l, r));
        if(r > mid) res = max(res, query(rchild(u), l, r));
        return res;
    }
};

int n, m;
ll c;
pair<int, ll> mar[N];
ll f[N];

void solve() {
    scanf("%d%lld%d", &n, &c, &m);
    for (int i=1; i<=m; i++) {
        scanf("%d%lld", &mar[i].fi, &mar[i].se);
    }

    vector<ll> a(n + 1, -inf);
    SegmentTree tr_l, tr_r;
    a[1] = c;
    tr_l.init(n, a);
    a[1] = -c;
    tr_r.init(n, a);
    ll res = 0;
    for (int i=1; i<=m; i++) {
        auto [t, p] = mar[i];
        ll dp1 = tr_l.query(1, 1, t) - c * t;
        ll dp2 = tr_r.query(1, t, n) + c * t;
        f[i] = max(dp1, dp2) + p;
        res = max(res, f[i]);
        tr_l.modify(1, t, f[i] + c * t);
        tr_r.modify(1, t, f[i] - c * t);
    }

    printf("%lld\n", res);
}

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    std::cout.tie(nullptr);

    // multiple case
    // int t; scanf("%d", &t);
    // while(t--) {
    //     solve();
    // }

    // single case
    solve();

    return 0;
}
posted @ 2024-05-17 10:33  1v7w  阅读(25)  评论(0编辑  收藏  举报