[BZOJ1568][JSOI2008]Blue Mary开公司(线段树)

Address

https://www.lydsy.com/JudgeOnline/problem.php?id=1568

Solution

首先将问题简化:
一个二维平面,每次加入一条直线,在 x = 1 处的横坐标为 S 且斜率为 P ,或求在 x = T 处线段的最高点的 y 坐标。
我们考虑线段树,线段树每个节点维护一条线段,这样询问时指需要从根走到对应的叶子节点,在走的过程中取 max 即可。
而插入直线时,容易发现我们要讨论的关键问题就是处理直线相交
有一个巧妙的方法:
假设我们在节点 p 对应的区间 [ l , r ] 内插入线段 s 时,发现节点 p 内已经有线段 t
分类讨论:
(1) s t 下方:这时候 s 在节点 p 对应的区间里一定不是最高点。停止操作。
(2) s t 上方:这时候 t 在节点 p 对应的区间里一定不是最高点。用 s 替换 t 并停止操作。
(3) s t 交点的横坐标为 x
m i d = l + r 2
①如果 x m i d :保留 s t 两条线段在 [ m i d + 1 , r ] 范围内靠上的线段,并把另一条线段推到 p 的左子节点,往左子节点继续执行一样的操作。
②如果 x m i d + 1 :保留 s t 两条线段在 [ l , m i d ] 范围内靠上的线段,并把另一条线段推到 p 的右子节点,往右子节点继续执行一样的操作。
③否则 m i d < x < m i d + 1 ,执行①或②均可。
复杂度 O ( n log T )
线段树上的这种操作是 ZJ 省的 OI 界神犇李超提出的,
因此这被称为「李超树」或「李超线段树」「超哥线段树」。

Code

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define p2 p << 1
#define p3 p << 1 | 1
using namespace std;
const int N = 2e5 + 5;
int n;
double fr[N], to[N];
char s[N];
void ins(int l, int r, double up, double dn, int p) {
    if (l == r) return (void) (fr[p] = to[p] = max(fr[p], up));
    if (up <= fr[p] && dn <= to[p]) return;
    if (up >= fr[p] && dn >= to[p])
        return (void) (fr[p] = up, to[p] = dn);
    int mid = l + r >> 1;
    double k1 = 1.0 * (dn - up) / (r - l),
        k2 = 1.0 * (to[p] - fr[p]) / (r - l);
    double b1 = up - k1 * l, b2 = fr[p] - k2 * l;
    double ist = (b1 - b2) / (k2 - k1);
    if ((ist <= mid) ^ (up > fr[p]))
        swap(up, fr[p]), swap(dn, to[p]), swap(k1, k2);
    if (ist <= mid) ins(l, mid, up, up + k1 * (mid - l), p2);
    else ins(mid + 1, r, up + k1 * (mid + 1 - l), dn, p3);
}
double query(int l, int r, int pos, int p) {
    if (l == r) return fr[p];
    double res = fr[p] + (to[p] - fr[p]) * (pos - l) / (r - l);
    int mid = l + r >> 1;
    if (pos <= mid) res = max(res, query(l, mid, pos, p2));
    else res = max(res, query(mid + 1, r, pos, p3));
    return res;
}
int main() {
    double le, ri; int x;
    cin >> n;
    while (n--) {
        scanf("%s", s);
        if (s[0] == 'P') scanf("%lf%lf", &le, &ri),
            ins(1, 50000, le, le + ri * 49999, 1);
        else scanf("%d", &x),
            printf("%d\n", (int) (query(1, 50000, x, 1) / 100));
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/xyz32768/article/details/81286205