求助,李超树95pts。代码里有注释qwq,输出比结果大。
查看原帖
求助,李超树95pts。代码里有注释qwq,输出比结果大。
439088
Gensokyo_Alice楼主2020/12/12 16:27

翻过题解了,,,似乎没人写李超树。。。

因为询问的是整点,所以直线的 kb 都是用 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;
}

2020/12/12 16:27
加载中...