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\) 个城镇时候的最大受益。
这里我们发现其实枚举第 \(i\) 个活动时候,只需要考虑最终在第 \(t_i\) 个城镇就好。这时候复杂度就降为了 \(O(nm)\),方程变为了
优化第一维就变成了\(dp[t_i] = max_{1 \le j \le n} \{ dp[j] - c|j-t_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;
}