LGOJ4254 [JSOI2008]Blue Mary开公司
题意
题解
本题是李超线段树模板
李超线段树解决的问题
考虑下面一个问题:
定义一个坐标系,有 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;
}

查看13道真题和解析