「bzoj1568」「jsoi2008」blue mary开公司

题目传送门

李超线段树模板题。

题意

维护一个二维平面,支持两种操作:插入一条直线((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 <algorithm>
#define N 500005
int n, m, x;
double k[N], b[N];
char opt[15];
struct {
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("%dn", int(T.Query(1, 1, 50000, x) / 100));
}
}


深深明白自己的弱小