LGOJ4254 [JSOI2008]Blue Mary开公司

题意

Link_Luogu

题解

本题是李超线段树模板

李超线段树解决的问题

考虑下面一个问题:

定义一个坐标系,有 m 次操作

操作 1:添加一条直线;

操作 2:求 x=x0 这条直线和其他直线的交点的最高纵坐标。

要求时间复杂度 log 级别,可以用线段树维护。

李超线段树的原理

对于一个区间,我们维护该区间的所有直线中,没有被其他直线覆盖的从上往下去看在x轴上的投影最大的直线(称为标记直线)。

其实这条直线的意义就是对这个区间内大多数点而言最优的直线,但也不一定是最优的直线,更优解可能在更小的区间内。

现在插入一条直线(称为新直线)到了一个区间,尝试更改该区间的标记直线。

分为几种情况:

1.新直线完全覆盖标记直线,直接更新并return,不用继续向下更新。

2.新直线完全被覆盖,那么新直线无用,直接return。

3.不是上面两种情况,判断两条直线哪一个投影大一些,作为新的标记直线,再递归下去处理。

注意,"3." 里面递归下去处理的是投影小的那一个哦(变向 Pushdown emmm...)

询问时直接整个线段树包含 x0 的区间所存的线段取 max 即可(相当于标记永久化中的单点查询)。

其实相当于一个标记永久化的思想(使用永久化实现对“子树的类覆盖”)。

本质上我们可以考虑一个暴力,就是每一次把这条直线所包含的所有线段树的区间暴力更新“标记直线”,这样的话在最下层答案肯定是正确的。

然后我们易发现上面的标记永久化和这个暴力是等价的(影响的都是一个子树);依次类推,如果插入的不是直线而是线段,可以把线段拆到 log 个被这个线段完全覆盖的区间上,在这些区间上这些线段都可以看成是直线,复杂度多一个 log。

最后如果是要询问一个区间的最大值也很好处理,如果是插入的是直线,只需询问区间的两个端点就可以了;线段的话只要插入的时候算出区间两个端点的值然后 Pushup,同时还要进行查询 max 的标记永久化。

要注意斜率是在 x = 0 处的取值

代码

#include <bits/stdc++.h>

using namespace std;

const int N = 1e5 + 10, MAXVAL = 50008;

struct Line {
    double k, b;
    template <class TT>
    inline double val(TT x) { return k * x + b; }
} zcx, Tree[N * 4 + 10];

#define lson (x << 1)
#define rson (x << 1 | 1)

void Update(int x, int nl, int nr) {
    if (zcx.val(nl) <= Tree[x].val(nl) && zcx.val(nr) <= Tree[x].val(nr)) return;
    if (zcx.val(nl) >= Tree[x].val(nl) && zcx.val(nr) >= Tree[x].val(nr)) {
        Tree[x] = zcx;
        return;
    }
    int nm = (nl + nr) >> 1;
    if (zcx.val(nl) >= Tree[x].val(nl) && zcx.val(nm) >= Tree[x].val(nm)) {
        swap(Tree[x], zcx);
        Update(rson, nm + 1, nr);
    } else if (zcx.val(nl) <= Tree[x].val(nl) && zcx.val(nm) <= Tree[x].val(nm)) {
        Update(rson, nm + 1, nr);
    } else if (zcx.val(nm + 1) >= Tree[x].val(nm + 1) && zcx.val(nr) >= Tree[x].val(nr)) {
        swap(Tree[x], zcx);
        Update(lson, nl, nm);
    } else {
        Update(lson, nl, nm);
    }
    /* 下面是写假了的
    if ((zcx.val(nl) >= Tree[x].val(nl)) != (zcx.val(nm) >= Tree[x].val(nm))) {
        if (zcx.val(nm + 1) >= Tree[rson].val(nm + 1) && zcx.val(nr) >= Tree[rson].val(nr))
            Tree[rson] = zcx;
        Update(lson, nl, nm);
    } else {
        if (zcx.val(nl) >= Tree[lson].val(nl) && zcx.val(nm) >= Tree[lson].val(nm))
            Tree[lson] = zcx;
        Update(rson, nm + 1, nr);
    } */
}

double Query(int x, int nl, int nr, int ed) {
    if (nl == nr) return Tree[x].val(ed);
    int nm = (nl + nr) >> 1;
    double qsum = Tree[x].val(ed);
    if (ed <= nm) return max(qsum, Query(lson, nl, nm, ed));
    else return max(qsum, Query(rson, nm + 1, nr, ed));
}

#undef lson
#undef rson

char qrystr[20];
int Qrytimes;
/* 注意题面中 n 不一定是值域大小,这是个坑 */

int main()
{
    freopen("LGOJ4254.in", "r", stdin);
    freopen("LGOJ4254.out", "w", stdout);

    scanf("%d", &Qrytimes);
    while (Qrytimes--) {
        scanf("%s", qrystr);
        if (!strcmp(qrystr, "Query")) {
            int qrypos;
            scanf("%d", &qrypos);
            long long fans = (long long)(Query(1, 1, MAXVAL /* there is no n */, qrypos) / 100.0);
            printf("%lld\n", fans);
        } else {
            scanf("%lf%lf", &zcx.b, &zcx.k);
            zcx.b -= zcx.k; /* 斜率是在 x = 0 处的取值要注意!!! */
            Update(1, 1, MAXVAL);
        }
    }

    return 0;
}
全部评论

相关推荐

11-11 14:21
西京学院 C++
Java抽象练习生:教育背景放最前面,不要耍小聪明
点赞 评论 收藏
分享
点赞 收藏 评论
分享
牛客网
牛客企业服务