[luoguP4097/HEOI2013] 李超线段树
题意
在平面上给定一些线段,给定 \(k\),查询各线段中与直线 \(x=k\) 交点最靠上的线段编号,强制在线。
sol
该类问题都可以使用李超线段树来解决。
对于李超线段树中的每一个节点,设其对应的区间中点为 \(mid\),那么各线段中与直线 \(x=mid\) 交点最靠上的线段编号一定被记录在该节点或该节点的祖宗节点,这样在查询操作时,只需要在线段树上二分,记录下最靠上的交点极其编号即可。
在插入时,首先要对线段进行拆分,使得进行更新操作时的每一个区间都被该线段完全覆盖,然后进行更新操作。容易发现,对于相同区间的两条线段,中点更靠上的那条一定会至少在整个左半区间或右半区间都在另一条线段上方,因此为保证复杂度,如果出现插入的线段比已记录的线段的中点更靠上(或中点重合,编号更小),就将其交换。
但是被替换的线段也可能会更新另半个区间的记录值,因此仍需要将其下传,根据前文易得,拆分和更新操作的时间复杂度均为 \(O(\log n)\),因此总的时间复杂度为 \(O(n \log^2 n)\)。
代码
#include <iostream>
#include <algorithm>
#include <cstring>
#define x first
#define y second
using namespace std;
typedef pair<double, int> PDI;
const int N = 39989, M = 100005, INF = 1e9;
const double eps = 1e-6;
int n, idx;
int tr[(N + 5) * 4];
struct Segment {
double k, b;
} S[M];
int cmp(double a, double b) {
if (abs(a - b) <= eps) return 0;
else if (a > b) return 1;
else return -1;
}
PDI Max(PDI A, PDI B) {
if (cmp(A.x, B.x)) return cmp(A.x, B.x) == 1 ? A : B;
else return A.y < B.y ? A : B;
}
void decode(int lastans, int &x, int mod){
x = (x + lastans - 1) % mod + 1;
}
double calc(int u, int p) {
// printf("(%d: y=%.2lfx + %.2lf %d %.6lf)\n", u, S[u].k, S[u].b, p, p * S[u].k + S[u].b);
return p * S[u].k + S[u].b;
}
void add(int u, int x0, int y0, int x1, int y1) {
if (x0 == x1) S[u] = {0, max(1.0 * y0, 1.0 * y1)};
else S[u] = {1.0 * (y1 - y0) / (x1 - x0), (1.0 * y0 * x1 - 1.0 * y1 * x0) / (x1 - x0)};
}
void upd(int u, int l, int r, int x) {
int mid = l + r >> 1;
int res = cmp(calc(x, mid), calc(tr[u], mid));
if (res == 1 || !res && x < tr[u]) swap(x, tr[u]);
int lres = cmp(calc(x, l), calc(tr[u], l)), rres = cmp(calc(x, r), calc(tr[u], r));
if (lres == 1 || !lres && x < tr[u]) upd(u << 1, l, mid, x);
if (rres == 1 || !rres && x < tr[u]) upd(u << 1 | 1, mid + 1, r, x);
// printf("[%d %d %d %d]\n", u, l, r, tr[u]);
}
void update(int u, int l, int r, int L, int R, int x) {
if (L <= l && r <= R) {
upd(u, l, r, x);
return ;
}
int mid = l + r >> 1;
if (L <= mid) update(u << 1, l, mid, L, R, x);
if (R > mid) update(u << 1 | 1, mid + 1, r, L, R, x);
}
PDI query(int u, int l, int r, int x) {
if (l == r) return {calc(tr[u], x), tr[u]};
int mid = l + r >> 1;
if (x <= mid) return Max({calc(tr[u], x), tr[u]}, query(u << 1, l, mid, x));
if (x > mid) return Max({calc(tr[u], x), tr[u]}, query(u << 1 | 1, mid + 1, r, x));
}
int main(){
scanf("%d", &n);
int lastans = 0;
for (int i = 1; i <= n; i ++ ) {
int op;
scanf("%d", &op);
if (op == 1) {
int x0, y0, x1, y1;
scanf("%d%d%d%d", &x0, &y0, &x1, &y1);
decode(lastans, x0, N), decode(lastans, y0, INF), decode(lastans, x1, N), decode(lastans, y1, INF);
if (x0 > x1) swap(x0, x1), swap(y0, y1);
add( ++ idx, x0, y0, x1, y1);
update(1, 1, N, x0, x1, idx);
}
else {
int x;
scanf("%d", &x);
decode(lastans, x, N);
lastans = query(1, 1, N, x).y;
printf("%d\n", lastans);
}
}
}
蒟蒻犯的若至错误
- 横纵坐标的在线解码方式不同,没审题(

浙公网安备 33010602011771号