/**
题意:
有n+1个城市一直线上,现在从0号城市按顺序走到n号城市,从0号城市到i号城市没走一个单位距离需要消耗ai个糖果,
每个城市都可以买/卖糖果,价格分别是buyi和selli,selli<buyi。身上最多只能带C个糖果,在起点0号城市身上没有糖果,
问到达n号城市的最小花费。
思路:
考虑当前点, 每次将当前点的糖果补满,补满之前考虑之前剩余的糖果:
1. 上一个点到达当前点需要消耗xi的糖果, 消耗购买价格小的,总消费才能尽量小,剩余一些糖果
2. 糖果在之前城市的购买价格高于当前点的购买价格, 以原价售出(相当于不买)
3. 糖果在之前城市的购买价格低于当前点的购买价格,假设当前位置是r,该糖果是在l点购买的,
那么这个糖果有中可能:
(1) 该糖果在后面被消耗了
(2) 该糖果可以在之前的某个城市以一种更优的售出之后再到当前点购买等价交换之后使得购买价格更低
(3) 该糖果最终到达n号城市
1、2点用单调队列存储(购买价格由低到高)之后就可以解决,3中(1)(3)都是不用考虑的,考虑(2) :
设单调队列中的糖果购买价格最低的是在id这个位置,剩下res颗,在id购买价格是val,如果等价交换之后
在后面的消耗中消耗价格更低,那么有buy[i]-(sell_max[id, i]-val)<=val => buy[i]<=sell_max[id, i]
(sell_max[l, r]是在区间[l,r]售出价格最高的地方),假设sell_max[id, i] = sell[x] (x这个位置取最大), 而
且有buy[i] <= sell_max[id, i]那么这个糖果就在x这个位置售出再用当前点的糖果补上,因为单调队列中的
价格从低到高排列而且位置中小到大排列, 从左往右处理下就行了
*/
#include<bits/stdc++.h>
typedef long long ll;
const int maxn = 2e5 + 10;
using namespace std;
struct P {
ll id, res, val;
P() {}
P(ll i, ll r, ll v) : id(i), res(r), val(v) {}
} que[maxn];
ll n, m, T, kase = 1;
ll b[maxn], s[maxn];
ll st[maxn][20], dis[maxn];
void init() {
for(ll i = 1; (1 << i) <= n + 1; i++) {
ll sz = (1 << i), s = sz >> 1;
for(ll j = 0; j + sz - 1 <= n; j++) {
ll x = j + s;
st[j][i] = max(st[j][i - 1], st[x][i - 1]);
}
}
}
ll query(ll l, ll r) {
ll tot = r - l + 1, ans = b[l], len = 0;
for( ; (1 << len) <= tot; len++); len--;
for( ; len >= 0; len--) if(l + (1 << len) - 1 <= r) {
ans = max(ans, st[l][len]); l = l + (1 << len);
}
return ans;
}
int main() {
scanf("%lld", &T);
while(T--) {
scanf("%lld %lld", &n, &m);
for(ll i = 1; i <= n; i++) scanf("%lld", &dis[i]);
for(ll i = n; i >= 1; i--) dis[i] = dis[i] - dis[i - 1];
for(ll i = 0; i <= n; i++) { scanf("%lld %lld", &b[i], &s[i]); st[i][0] = s[i]; }
init();
ll tot = 0, l = 0, r = 0, ans = 0;
for(ll i = 0; i <= n; i++) {
tot -= dis[i];
while(l < r && dis[i]) {
if(que[l].res > dis[i]) {
que[l].res -= dis[i]; break;
} else {
dis[i] -= que[l].res; l++;
}
}
while(l < r && que[r - 1].val >= b[i]) {
tot -= que[r - 1].res;
ans += query(que[r - 1].id, i) * que[r - 1].res;
r--;
}
while(l < r) {
ll id = que[l].id, res = que[l].res, val = que[l].val;
ll ds = query(id, i);
if(ds < b[i]) break;
ans += ds * res; tot -= res; l++;
}
que[r++] = P(i, m - tot, b[i]);
ans -= (ll)(m - tot) * b[i];
tot = m;
}
while(l < r) {
ll id = que[l].id, res = que[l].res, val = que[l].val; l++;
ans += query(id, n) * res;
}
printf("%lld\n", -ans);
}
return 0;
}
HDU5380 Travel with candy(单调队列 + 贪心)
最新推荐文章于 2018-05-08 20:20:38 发布