Address
https://www.lydsy.com/JudgeOnline/problem.php?id=1568
Solution
首先将问题简化:
一个二维平面,每次加入一条直线,在
x=1
x
=
1
处的横坐标为
S
S
且斜率为 ,或求在
x=T
x
=
T
处线段的最高点的
y
y
坐标。
我们考虑线段树,线段树每个节点维护一条线段,这样询问时指需要从根走到对应的叶子节点,在走的过程中取 即可。
而插入直线时,容易发现我们要讨论的关键问题就是处理直线相交。
有一个巧妙的方法:
假设我们在节点
p
p
对应的区间 内插入线段
s
s
时,发现节点 内已经有线段
t
t
。
分类讨论:
(1) 在
t
t
下方:这时候 在节点
p
p
对应的区间里一定不是最高点。停止操作。
(2) 在
t
t
上方:这时候 在节点
p
p
对应的区间里一定不是最高点。用 替换
t
t
并停止操作。
(3) 和
t
t
交点的横坐标为 。
设
mid=⌊l+r2⌋
m
i
d
=
⌊
l
+
r
2
⌋
。
①如果
x≤mid
x
≤
m
i
d
:保留
s
s
和 两条线段在
[mid+1,r]
[
m
i
d
+
1
,
r
]
范围内靠上的线段,并把另一条线段推到
p
p
的左子节点,往左子节点继续执行一样的操作。
②如果 :保留
s
s
和 两条线段在
[l,mid]
[
l
,
m
i
d
]
范围内靠上的线段,并把另一条线段推到
p
p
的右子节点,往右子节点继续执行一样的操作。
③否则 ,执行①或②均可。
复杂度
O(nlogT)
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;
}