抱歉,您的浏览器无法访问本站

本页面需要浏览器支持(启用)JavaScript


了解详情 >

题目传送门

李超线段树模板题。

题意

维护一个二维平面,支持两种操作:插入一条直线(\(y=kx+b\));询问当前插入的所有直线\(x=x_0\)时最大的纵坐标的值。

题解

李超线段树。

一个节点同样表示一个区间\([l,r]\),记录的是一条直线\(id[u]\),这条直线是\(x=mid\)时纵坐标最大的直线。

插入一条直线时,流程如下:

  1. 直线\(id[u]\)\(i\)没有交点:直接取\(y\)值大的,退出。
  2. 如果当\(x=mid\)时,直线\(i\)\(y\)值比\(id[u]\)大,那么交换\(id[u],i\)
  3. 如果\(id[u]\)\(i\)的交点在mid左边,即当\(x=l\)时,直线\(i\)\(y\)值大于等于\(id[u]\),那么递归处理\([l,mid]\),这种情况下其实\([mid+1,r]\)的直线要改变,但因为询问时是从叶子节点到根节点取\(max\)的,所以没有必要更新。
  4. 如果\(id[u]\)\(i\)的交点在mid右边,即当\(x=r\)时,直线\(i\)\(y\)值大于等于\(id[u]\),那么递归处理\([mid+1,r]\),此时没有3中的情况。

至于询问,已经提过,只要从叶节点到根取max就好了。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include <cstdio>
#include <algorithm>
#define N 500005
int n, m, x;
double k[N], b[N];
char opt[15];
struct Li_Chao_Segment_Tree{
int id[N];
int check(int u, int v, int x){
return k[u] * (x - 1) + b[u] <= k[v] * (x - 1) + b[v];
}
void Insert(int u, int l, int r, int x){
if (check(id[u], x, l) && check(id[u], x, r)) return id[u] = x, void(0);
if (l == r) return;
int mid = (l + r) >> 1;
if (check(id[u], x, mid)) std :: swap(id[u], x);
if (check(id[u], x, l)) Insert(u << 1, l, mid, x);
if (check(id[u], x, r)) Insert(u << 1 | 1, mid + 1, r, x);
}
double Query(int u, int l, int r, int x){
if (l == r) return k[l] * (x - 1) + b[l];
int mid = (l + r) >> 1;
double ans;
if (x <= mid) ans = Query(u << 1, l, mid, x);
else ans = Query(u << 1 | 1, mid + 1, r, x);
return std :: max(ans, k[id[u]] * (x - 1) + b[id[u]]);
}
}T;
int main(){
for (scanf("%d", &m); m--; ){
scanf("%s", opt);
if (opt[0] == 'P') ++n, scanf("%lf%lf", b + n, k + n), T.Insert(1, 1, 50000, n);
else scanf("%d", &x), printf("%d\n", int(T.Query(1, 1, 50000, x) / 100));
}
}

评论