P4088 [USACO18FEB]Slingshot 线段树+扫描线

Posted olinr

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了P4088 [USACO18FEB]Slingshot 线段树+扫描线相关的知识,希望对你有一定的参考价值。

(color{#0066ff}{ 题目描述 })

Farmer John最讨厌的农活是运输牛粪。为了精简这个过程,他产生了一个新奇的想法:与其使用拖拉机拖着装满牛粪的大车从一个地点到另一个地点,为什么不用一个巨大的便便弹弓把牛粪直接发射过去呢?(事实上,好像哪里不太对……) Farmer John的农场沿着一条长直道路而建,所以他农场上的每个地点都可以简单地用该地点在道路上的位置来表示(相当于数轴上的一个点)。FJ建造了(N)个弹弓((1 leq N leq 10^5)),其中第(i)个弹弓可以用三个整数(x_i)(y_i)以及(t_i)描述,表示这个弹弓可以在(t_i)单位时间内将牛粪从位置(x_i)发射到位置(y_i)

FJ有(M)堆牛粪需要运输((1 leq M leq 10^5))。第(j)堆牛粪需要从位置(a_j)移动到位置(b_j)。使用拖拉机运输牛粪,经过路程(d)需要消耗(d)单位时间。FJ希望通过对每一堆牛粪使用至多一次弹弓来减少运输时间。FJ驾驶没有装载牛粪的拖拉机的时间不计。

对这(M)堆牛粪的每一堆,在FJ可以在运输过程中使用至多一次弹弓的条件下,帮助FJ求出其最小运输时间。

(color{#0066ff}{输入格式})

输入的第一行包含(N)(M)。下面(N)行,每行用(x_i)(y_i)(t_i)(0 leq x_i, y_i, t_i leq 10^9))描述了一个弹弓。最后(M)行用(a_j)(b_j)描述了需要移动的牛粪。

(color{#0066ff}{输出格式})

输出(M)行,每堆牛粪一行,表示运输这堆牛粪需要的最短时间。

(color{#0066ff}{输入样例})

2 3
0 10 1
13 8 2
1 12
5 2
20 7

(color{#0066ff}{输出样例})

4
3
10  

(color{#0066ff}{数据范围与提示})

none

(color{#0066ff}{ 题解 })

我们设弹弓是从x到y,询问从l到r

那么如果不加弹弓,代价是(|r-l|)

如果借助弹弓,显然是(|r-y|+|l-x|+t)

看到绝对值,要想办法去掉

于是出现了4种情况

这里拿其中一种情况举个锤子(雾

(rge y 且lge x)时,拆开,(r-y+l-x+t=l+r-x-y+t)

于是我们把询问和弹弓的端点都离散化,并按照r排序

枚举询问,维护一个在弹弓上的指针,把所有小于当前询问r的y都加入线段树中

(x)为下标,插入(-x-y+t),经过排序,显然第一个条件已经满足,于是我们线段树上查询([1,l])的答案再加上(l)(r)即可

其余情况分类讨论,同理

// luogu-judger-enable-o2
#include<bits/stdc++.h>
#define LL long long
LL in() {
    char ch; LL x = 0, f = 1;
    while(!isdigit(ch = getchar()))(ch == '-') && (f = -f);
    for(x = ch ^ 48; isdigit(ch = getchar()); x = (x << 1) + (x << 3) + (ch ^ 48));
    return x * f;
}
const LL inf = 999999999999999LL;
const int maxn = 6e5 + 100;
struct Tree {
protected:
    struct node {
        node *ch[2];
        int l, r;
        LL min;
        node(int l = 0, int r = 0, LL min = inf): l(l), r(r), min(min) {}
        void *operator new(size_t) {
            static node *S = NULL, *T = NULL;
            return (S == T) && (T = (S = new node[1024]) + 1024), S++;
        }
        void upd() { min = std::min(ch[0]->min, ch[1]->min); }
    };
    node *root;
    void build(node *&o, int l, int r) {
        o = new node(l, r, inf);
        if(l == r) return;
        int mid = (l + r) >> 1;
        build(o->ch[0], l, mid), build(o->ch[1], mid + 1, r);
    }
    void lazy(node *o, int p, LL k) {
        if(o->r < p || o->l > p) return;
        if(o->l == o->r) return (void)(o->min = std::min(o->min, k));
        lazy(o->ch[0], p, k), lazy(o->ch[1], p, k);
        o->upd();
    }
    LL query(node *o, int l, int r) {
        if(o->r < l || o->l > r) return inf;
        if(l <= o->l && o->r <= r) return o->min;
        return std::min(query(o->ch[0], l, r), query(o->ch[1], l, r));
    }
    void clr(node *o) {
        o->min = inf;
        if(o->l == o->r) return;
        clr(o->ch[0]), clr(o->ch[1]);
    }
public:
    void build(int n) { build(root, 1, n); }
    void lazy(int p, LL k) { lazy(root, p, k); }
    LL query(int l, int r) { return query(root, l, r); }
    void clr() { clr(root); }
}s;
struct Q {
    int x, y;
    LL val;
    Q(int x = 0, int y = 0, LL val = 0): x(x), y(y), val(val) {}
    friend bool operator < (const Q &a, const Q &b) {
        return a.y == b.y? a.x < b.x : a.y < b.y;
    }
}q[maxn], d[maxn];
int n, m;
LL a[maxn];
LL ans[maxn];
int main() {
    n = in(), m = in();
    int x, y, t, len = 0;
    for(int i = 1; i <= n; i++) {
        x = in(), y = in(), t = in();
        d[i] = Q(x, y, t);
        a[++len] = x, a[++len] = y;
    
    }
    for(int i = 1; i <= m; i++) {
        x = in(), y = in();
        q[i] = Q(x, y, i);
        a[++len] = x, a[++len] = y;
        ans[i] = y > x? y - x : x - y;
    }
    std::sort(a + 1, a + len + 1);
    int _ = 1;
    for(int i = 2; i <= len; i++) if(a[i] != a[i - 1]) a[++_] = a[i];
    len = _;
    s.build(len);
    for(int i = 1; i <= n; i++) {
        d[i].x = std::lower_bound(a + 1, a + len + 1, d[i].x) - a;
        d[i].y = std::lower_bound(a + 1, a + len + 1, d[i].y) - a;
    }
    for(int i = 1; i <= m; i++) {
        q[i].x = std::lower_bound(a + 1, a + len + 1, q[i].x) - a;
        q[i].y = std::lower_bound(a + 1, a + len + 1, q[i].y) - a;
    }   
    std::sort(d + 1, d + n + 1);
    std::sort(q + 1, q + m + 1);
    int now = 1;
    for(int i = 1; i <= m; i++) {
        while(now <= n && d[now].y <= q[i].y) {
            s.lazy(d[now].x, - a[d[now].x] - a[d[now].y] + d[now].val);
            now++;
        }
        ans[q[i].val] = std::min(ans[q[i].val], a[q[i].x] + a[q[i].y] + s.query(1, q[i].x));
    }
    now = 1, s.clr();
    for(int i = 1; i <= m; i++) {
        while(now <= n && d[now].y <= q[i].y) {
            s.lazy(d[now].x, a[d[now].x] - a[d[now].y] + d[now].val);
            now++;
        }
        ans[q[i].val] = std::min(ans[q[i].val], - a[q[i].x] + a[q[i].y] + s.query(q[i].x, len));
    }
    now = n, s.clr();
    for(int i = m; i >= 1; i--) {
        while(now >= 1 && d[now].y >= q[i].y) {
            s.lazy(d[now].x, - a[d[now].x] + a[d[now].y] + d[now].val);
            now--;
        }
        ans[q[i].val] = std::min(ans[q[i].val], a[q[i].x] - a[q[i].y] + s.query(1, q[i].x));
    }
    now = n, s.clr();
    for(int i = m; i >= 1; i--) {
        while(now >= 1 && d[now].y >= q[i].y) {
            s.lazy(d[now].x, a[d[now].x] + a[d[now].y] + d[now].val);
            now--;
        }
        ans[q[i].val] = std::min(ans[q[i].val], - a[q[i].x] - a[q[i].y] + s.query(q[i].x, len));
    }
    
    for(int i = 1; i <= m; i++) printf("%lld
", ans[i]);
    return 0;
}

以上是关于P4088 [USACO18FEB]Slingshot 线段树+扫描线的主要内容,如果未能解决你的问题,请参考以下文章

P4271 [USACO18FEB]New Barns

P4265 [USACO18FEB]Snow Boots S

[luogu4264][USACO18FEB]Teleportation

P4269 [USACO18FEB]Snow Boots G

题解[USACO18FEB]New Barns

[USACO18FEB]Snow Boots S