洛谷P7480 四月赛 Reboot from Blue

题目

在这里插入图片描述
题目链接

解题思路

首先油价是大于0的,油箱是无限的。因此我们不会从油价低的地方到油价高的地方去加油。我们可以根据每个地方油价的从高到低依次更新。
考虑某次到达点t后,其油价是v,那么对另一点s的贡献是v*abs(t-s)。这是两条直线。
考虑到我们是动态的确定点,并添加直线,因此维护下凸壳即可。
使用线段添加的李超树,复杂度 O ( n l o g 2 n ) O(nlog^2n) O(nlog2n)

代码

#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;

#define lson rt * 2
#define rson rt * 2 + 1

typedef long long ll;

const int N = 1e5 + 100;

int n, s, t, tp, cnt;
int has[N], val[N];
ll dp[N];

struct Line {
	ll k, b;
	Line() { k = 0; b = 1e18; }
	Line(ll k, ll b) :k(k), b(b) {}
	ll cal(ll x) {
		return k * x + b;
	}
}tree[N * 4];

void insert(Line x, int l, int r, int rt) {
	int m = (l + r) / 2;
	if (tree[rt].cal(has[m]) > x.cal(has[m])) swap(tree[rt], x);
	if (l == r) return;
	if (tree[rt].cal(has[l]) > x.cal(has[l])) insert(x, l, m, lson);
	else insert(x, m + 1, r, rson);
}

void insert(int rl, int rr, Line x, int l, int r, int rt) {
	if (rl == l && rr == r) return void(insert(x, l, r, rt));
	int m = (l + r) / 2;
	if (rr <= m) insert(rl, rr, x, l, m, lson);
	else if (m < rl) insert(rl, rr, x, m + 1, r, rson);
	else {
		insert(rl, m, x, l, m, lson);
		insert(m + 1, rr, x, m + 1, r, rson);
	}
}

void query(int id, Line &res, int l, int r, int rt) {
	if (tree[rt].cal(has[id]) < res.cal(has[id])) res = tree[rt];
	if (l == r) return;
	int m = (l + r) / 2;
	if (id <= m) query(id, res, l, m, lson);
	else query(id, res, m + 1, r, rson);
}

struct node {
	int w, id;
}sa[N];

bool operator < (node a, node b) {
	return a.w > b.w;
}

int get(int id) {
	return lower_bound(has + 1, has + tp + 1, id) - has;
}

int main() {
	//freopen("0.txt", "r", stdin);
	scanf("%d%d%d", &n, &s, &t);
	for (int i = 1; i <= n; i++) scanf("%d%d", &sa[i].w, &sa[i].id), has[++tp] = sa[i].id;
	has[++tp] = s; has[++tp] = t;
	sort(has + 1, has + tp + 1);
	tp = unique(has + 1, has + tp + 1) - has - 1;
	memset(val, 0x3f3f3f3f, sizeof(val));
	s = get(s); t = get(t);
	for (int i = 1; i <= n; i++) {
		sa[i].id = get(sa[i].id);
		val[sa[i].id] = min(val[sa[i].id], sa[i].w);
		if (sa[i].id == s) sa[i].w = 0;
	}
	sort(sa + 1, sa + n + 1);
	insert(s, tp, Line(val[s], -1LL * has[s] * val[s]), 1, tp, 1);
	insert(1, s, Line(-val[s], 1LL * has[s] * val[s]), 1, tp, 1);
	for (int i = 1; sa[i].w; i++) {
		if (sa[i].w != val[sa[i].id]) continue;
		Line res; int id = sa[i].id;
		query(id, res, 1, tp, 1);
		dp[id] = res.cal(has[id]);
		insert(1, id, Line(-val[id], dp[id] + 1LL * has[id] * val[id]), 1, tp, 1);
		insert(id, tp, Line(val[id], dp[id] - 1LL * has[id] * val[id]), 1, tp, 1);
	}
	Line res;
	query(t, res, 1, tp, 1);
	printf("%lld\n", res.cal(has[t]));
	return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值