翻过题解了,,,似乎没人写李超树。。。
因为询问的是整点,所以直线的 k
和 b
都是用 long long
维护的。
注释在代码里,感谢。
/*
Name:
Author: Gensokyo_Alice
Date:
Description:
*/
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <vector>
#include <ctime>
#include <stack>
#include <queue>
#include <set>
#include <map>
using namespace std;
typedef long long ll;
const ll MAXN = (1LL << 20) + 10, INF = 0x3f3f3f3f3f3f3f3f;
ll N, S, tim[MAXN], c[MAXN], d[MAXN], f[MAXN];
vector <ll> q;
struct line {
ll k, b;
line () {}
line (ll _k, ll _b): k(_k), b(_b) {}
ll calc(ll pos) {return k * q[pos-1] + b;}
};
struct nod {
ll l, r;
line L;
nod () {}
nod (ll _l, ll _r, line _L):l(_l), r(_r), L(_L) {}
} t[MAXN << 2];
void read(long long& x) {
char s = getchar(); x = 0; long long f = 1;
for (; !isdigit(s); s = getchar()) if (s == '-') f = -1;
for (; isdigit(s); s = getchar()) x = 10 * x + (s - '0');
x *= f;
}
void build(ll, ll, ll);
void ins(ll, ll, ll, line);
ll ask(ll, ll);
int main() {
read(N); read(S);
for (ll i = 1; i <= N; i++) scanf("%lld%lld", tim+i, c+i), tim[i] += tim[i-1], c[i] += c[i-1], q.push_back(tim[i]); // 读入。
sort(q.begin(), q.end());
q.erase(unique(q.begin(), q.end()), q.end());
for (ll i = 1; i <= N; i++) d[i] = lower_bound(q.begin(), q.end(), tim[i]) - q.begin() + 1; // 离散化。
build(1, 1, N); memset(f, 0x3f, sizeof(f)); f[0] = 0;
for (ll i = 1; i <= N; i++) {
f[i] = min(f[i], min(ask(1, d[i]) + S * c[N] + tim[i] * c[i], S * c[N] + tim[i] * c[i])); // 这里是DP式子的地方,其中S*c[N]+tim[i]*c[i]是从0开始的,ask(1, d[i])是寻找最优决策点。
ins(1, 1, N, line(-c[i], f[i] - c[i] * S)); // 插入直线。
}
printf("%lld\n", f[N]);
// for (ll i = 1; i <= N; i++) printf("%lld ", f[i]);
return 0;
}
void build(ll node, ll l, ll r) { // 建树,初始值为INF。
t[node] = nod(l, r, line(0, INF));
if (l == r) return;
ll mid = (l + r) >> 1;
build(node << 1, l, mid);
build(node << 1 | 1, mid + 1, r);
}
void ins(ll node, ll a, ll b, line seg) { // 插入线段。
ll mid = (t[node].l + t[node].r) >> 1;
if (a <= t[node].l && t[node].r <= b) {
ll segl = seg.calc(t[node].l), segr = seg.calc(t[node].r);
ll ql = t[node].L.calc(t[node].l), qr = t[node].L.calc(t[node].r);
if (segl < ql && segr < qr) swap(t[node].L, seg); // 全完胜出
else if (segl < ql || segr < qr) { // 部分胜出,考虑下传。
ll segm = seg.calc(mid), qm = t[node].L.calc(mid);
if (segm < qm) swap(seg, t[node].L); // 交换最优线段。
if (seg.calc(t[node].l) <= t[node].L.calc(t[node].l)) ins(node << 1, a, b, seg); // 考虑向哪个儿子下传。
else ins(node << 1 | 1, a, b, seg);
}
} else {
if (a <= mid) ins(node << 1, a, b, seg);
if (mid < b) ins(node << 1 | 1, a, b, seg);
}
}
ll ask(ll node, ll x) { // 李超树的询问,返回值是最小的f[j]-c[j]*S-t[i]*c[j];
if (t[node].l == t[node].r) return t[node].L.calc(x);
ll ret = t[node].L.calc(x), mid = (t[node].l + t[node].r) >> 1;
if (x <= mid) ret = min(ask(node << 1, x), ret);
else ret = min(ask(node << 1 | 1, x), ret);
return ret;
}