Codeforces Round #775 - F. Serious Business

这篇博客探讨了一种巧妙的方法,通过拆分三行权值为前缀和的函数s和f,解决从左上角到右下角的路径选择问题。通过定义dp数组来存储解锁区间操作后的最大收益,解决了解锁第二行特定区间成本最小化的最优化问题。作者在代码实现中分享了关键步骤和调试技巧。
摘要由CSDN通过智能技术生成

F. Serious Business

prob. :有 3 × n 3\times n 3×n个格子,每个格子有一个权值,你要从左上走到右下,每一步只能往右或往下走,刚开始的时候第二行是封锁的状态,你有一些可选的操作去解锁第二行从 [ L i , R i ] [L_i, R_i] [Li,Ri]的格子,每种操作的代价分别是 k i k_i ki, 问最大能获得的值是多少

ideas :非常巧妙将三行的权值拆成两个函数

pref表示每行的前缀和

s [ i ] = p r e f [ 0 ] [ i + 1 ] − p r e f [ 1 ] [ i ] s[i] = pref[0][i +1] - pref[1][i] s[i]=pref[0][i+1]pref[1][i]

f [ i ] = p r e f [ 1 ] [ i + 1 ] − p r e f [ 2 ] [ i ] + p r e f [ 2 ] [ n ] f[i] = pref[1][i + 1] - pref[2][i] + pref[2][n] f[i]=pref[1][i+1]pref[2][i]+pref[2][n]

最终的答案变成 m a x 0 ≤ i < j < n s [ i ] + f [ j ] − c o s t ( i , j ) max_{0\le i < j < n} s[i] + f[j] -cost(i, j) max0i<j<ns[i]+f[j]cost(i,j), cost指第二行解锁区间 [ i , j ] [i, j] [i,j]的最小价值

// 在下面具体代码实现的时候s和f的+1-1可能和上述有些不同
在这里插入图片描述

直接用s+f更新答案是只解锁一个区间时候的答案,dp记录的是多个区间时候的答案(这里为了少转移只对每个操作的r端点更新值

dp是s-cost,对于每个操作 L i , R i L_i, R_i Li,Ri,用s和dp更新 d p [ R ] dp[R] dp[R], 对应的s是 [ L , R ] [L, R] [L,R]区间的因为他还在第0行没有走下来,dp已经在第1行了,所以是由 [ L − 1 , R − 1 ] [L-1, R-1] [L1,R1]转移的,(R这里不用算进去但可能算进去也没啥区别,因为-k之后肯定比本身小)

注意更新dp的时候要对区间排序

写死我了,我好菜啊,谢谢npy帮我debug,“有些人被迫参与全程”

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const ll N = 5e5 + 50;
const ll inf = 0x3f3f3f3f3f3f3f3f;

struct node {
    ll l, r;
    ll s, f, sf;
};

node tr[N << 2];

ll s[N], f[N], dp[N];

void pushup(ll u) {
    tr[u].s = max(tr[u << 1].s, tr[u << 1 | 1].s);
    tr[u].f = max(tr[u << 1].f, tr[u << 1 | 1].f);
    tr[u].sf = max(max(tr[u << 1].sf, tr[u << 1 | 1].sf), tr[u << 1].s + tr[u << 1 | 1].f);
}

void build(ll u, ll l, ll r) {
    if (l == r) {
        tr[u] = {l, r, s[l], f[l], s[l] + f[l]};
    } else {
        tr[u] = {l, r, -inf, -inf, -inf};
        ll mid = (l + r) >> 1;
        build(u << 1, l, mid);
        build(u << 1 | 1, mid + 1, r);
        pushup(u);
    }
}

void modify(ll u, ll l, ll r, ll s) {
    if (tr[u].l >= l && tr[u].r <= r) {
        tr[u].s = max(tr[u].s, s);
        tr[u].sf = tr[u].s + tr[u].f;
        return;
    }
    ll mid = (tr[u].l + tr[u].r) >> 1;
    if (l <= mid) modify(u << 1, l, r, s);
    if (r > mid) modify(u << 1 | 1, l, r, s);
    pushup(u);
}

struct note {
    ll sf, s, f;
};

note querySF(ll u, ll l, ll r) {
    if (tr[u].l >= l && tr[u].r <= r) {
        return {tr[u].sf, tr[u].s, tr[u].f};
    }

    ll mid = (tr[u].l + tr[u].r) >> 1;
    ll ansSF = -inf, ansS = -inf, ansF = -inf, ss = -inf, ff = -inf;
    if (l <= mid) {
        auto tmp = querySF(u << 1, l, r);
        ansSF = max(ansSF, tmp.sf);
        ansS = max(ansS, tmp.s);
        ansF = max(ansF, tmp.f);
        ss = max(ss, tmp.s);
    }
    if (r > mid) {
        auto tmp = querySF(u << 1 | 1, l, r);
        ansSF = max(ansSF, tmp.sf);
        ansS = max(ansS, tmp.s);
        ansF = max(ansF, tmp.f);
        ff = max(ff, tmp.f);
    }
    ansSF = max(ansSF, ss + ff);
    return {ansSF, ansS, ansF};
}

ll a[5][N];
ll pref[5][N];

struct seg {
    ll l, r;
    ll k;
};

seg segs[N];

signed main() {
    ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);


    ll n, q;
    cin >> n >> q;

    bool checkflag = 0;
    for (ll i = 0; i < 3; ++i) {
        for (ll j = 1; j <= n; ++j) {
            cin >> a[i][j];
            if (a[i][j] > 0) checkflag = 1;
            pref[i][j] = pref[i][j - 1] + a[i][j];
        }
    }


    for (ll i = 1; i <= n; ++i) {
        s[i] = pref[0][i] - pref[1][i - 1];
        f[i] = pref[1][i] - pref[2][i - 1] + pref[2][n];
    }

    build(1, 1, n + 5);

    for (ll i = 0; i <= n; ++i) dp[i] = -inf; // init dp

    ll ans = -inf;
    for (ll i = 1; i <= q; ++i) { // refresh dp by s
        cin >> segs[i].l >> segs[i].r >> segs[i].k;
        ll l = segs[i].l, r = segs[i].r;
        ll k = segs[i].k;
        ans = max(ans, querySF(1, l, r).sf - k);
        ll tmp = querySF(1, l, r).s - k;
        dp[r] = max(tmp, dp[r]);
    }

    for (ll i = 0; i <= n; ++i) s[i] = dp[i];

    build(1, 1, n + 5);

    sort(segs + 1, segs + q + 1, [&](auto a, auto b) {
        return a.r < b.r;
    });

    for (ll i = 1; i <= q; ++i) { // refresh dp by dp
        ll l = segs[i].l, r = segs[i].r;
        ll k = segs[i].k;
        ll tmp = querySF(1, (l - 1 == 0 ? l : l - 1), (r - 1 == 0 ? r : r - 1)).s - k;
        modify(1, r, r, tmp);
        ans = max(ans, querySF(1, (l - 1 == 0 ? l : l - 1), r).sf - k);
    }

    cout << ans << endl;
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值