李超线段树学习笔记

  1. 1. 李超线段树学习笔记
    1. 1.1. 引入
    2. 1.2. 思想

李超线段树学习笔记

引入

李超线段树是一种解决解决二维平面直角坐标系中直线和线段的最值问题的数据结构,支持动态插入线段,查询某一横坐标上值最大(或最小)的直线标号或值的数据结构。

思想

首先想想暴力,也就是枚举一遍所有线段,复杂度 。但是很明显这个复杂度是可以优化的,在维护一个无序序列的区间最值时,我们可以用线段树来实现,那么我们也可以将线段树搬到平面坐标系上,来维护区间的最大值线段。下面给出具体实现思想:

  • 1.像线段树一样,把横坐标当做下标,每个节点维护这一区间上的最值,建树方法和线段树一样,只不过维护的是直线或线段。

  • 2.当插入新线段时,若当前区域没有线段,则直接插入。下面考虑有旧线段的情况,设新线段为 ,旧线段为

    • 若在左端点处 更优,那么 必然在左半区间中产生了交点, 只有在左区间才可能优于 ,递归到左儿子中进行下传;
    • 若在右端点处 更优,那么 必然在右半区间中产生了交点, 只有在右区间才可能优于 ,递归到右儿子中进行下传;
    • 若在左右端点处 都更优,那么 不可能成为答案,不需要继续下传。
Code
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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
#include <bits/stdc++.h>
using namespace std;
#define y1 y$$$$
#define y0 y_$_$
const double eps = 1e-9;
const int N = 1e5 + 10;
const int MOD1 = 39989;
const int MOD2 = 1e9;
const int tree_size = 4e4 + 10;
struct LINE {
double k, b;
LINE(double k = 0.0, double b = 0.0): k(k), b(b) {}
} line[N];
int cmp(double x, double y) {
if (x - y > eps) return 1;
else if (y - x > eps) return -1;
else return 0;
}
double calc(int line_num, int x) {
return line[line_num].k * x + line[line_num].b;
}
int cnt;
void add(int x0, int y0, int x1, int y1) {
++cnt;
if (x0 == x1) line[cnt] = LINE(0, max(y0, y1));
else {
double k = 1.0 * (y1 - y0) / (x1 - x0);
line[cnt] = LINE(k, y0 - k * x0);
}
}
#define ls(x) x<<1
#define rs(x) x<<1|1
class SegmentTree {
struct node {
int l, r;
int maxLine;
node(int l = 0, int r = 0, int maxLine = 0): l(l), r(r), maxLine(maxLine) {}
};
int get_cmp(int id1, int id2, int x) {
return cmp(calc(id1, x), calc(id2, x));
}
node *tree;
public:
SegmentTree(int _size) {
tree = new node[_size]();
}
void Build(int l, int r, int pos) {
tree[pos] = node(l, r, 0);
if (l == r) return;
int mid = (l + r) >> 1;
Build(l, mid, ls(pos));
Build(mid + 1, r, rs(pos));
}
void UpdateLine(int l, int r, int pos, int id) {
int mid = (l + r) >> 1;
if (get_cmp(id, tree[pos].maxLine, mid) == 1)swap(id, tree[pos].maxLine);
int cmpl = get_cmp(id, tree[pos].maxLine, l);
int cmpr = get_cmp(id, tree[pos].maxLine, r);
if (cmpl == 1 || (!cmpl && id < tree[pos].maxLine)) UpdateLine(l, mid, ls(pos), id);
if (cmpr == 1 || (!cmpr && id < tree[pos].maxLine)) UpdateLine(mid + 1, r, rs(pos), id);
}
void Update(int l, int r, int pos, int id) {
if (tree[pos].l >= l && tree[pos].r <= r) return (void) UpdateLine(tree[pos].l, tree[pos].r, pos, id);
int mid = (tree[pos].l + tree[pos].r) >> 1;
if (l <= mid) Update(l, r, ls(pos), id);
if (r > mid) Update(l, r, rs(pos), id);
}
pair<double, int>pmax(pair<double, int>x, pair<double, int>y) {
if (cmp(x.first, y.first) == 1) return x;
else if (cmp(x.first, y.first) == -1) return y;
else return x.second < y.second ? x : y;
}
pair<double, int>query(int pos, int x) {
if (tree[pos].l > x || tree[pos].r < x) return {0, 0};
double res = calc(tree[pos].maxLine, x);
if (tree[pos].l == tree[pos].r) return {res, tree[pos].maxLine};
return pmax({res, tree[pos].maxLine}, pmax(query(ls(pos), x), query(rs(pos), x)));
}
};
SegmentTree tree(tree_size * 4);
int n, op, x0, x1, y0, y1;
int lastans;
int k;
signed main() {
cin >> n;
tree.Build(1, MOD1, 1);
while (n--) {
cin >> op;
if (op == 0) {
cin >> k;
k = (k + lastans - 1) % MOD1 + 1;
cout << (lastans = tree.query(1, k).second) << endl;
} else {
cin >> x0 >> y0 >> x1 >> y1;
x0 = (x0 + lastans - 1) % MOD1 + 1;
x1 = (x1 + lastans - 1) % MOD1 + 1;
y0 = (y0 + lastans - 1) % MOD2 + 1;
y1 = (y1 + lastans - 1) % MOD2 + 1;
if (x0 > x1) swap(x0, x1), swap(y0, y1);
add(x0, y0, x1, y1);
tree.Update(x0, x1, 1, cnt);
}
}
}