李超线段树
用途:
用于二维坐标系维护多条线段。
算法:
本质上是采用标记永久化,
对每个线段树节点维护一个标记表示该区间存在这一条线段,
查询时从上到下经过节点的标记即为该横坐标上可能经过的线段。
下面需在标记(线段)间的比较上作考虑:建议画图理解
此时对于一个区间\([l, r]\),
找出中点\(mid\),以两线段(新线段,原标记)在中点大小关系分:下的为\(f\),上的为\(g\)(等于时都可)。
下面约定\(f[l]\)为在\(f\)线段上\(x=l\)时的纵坐标,
先给该点打上标记\(g\)。
如果\(f\)被\(g\)全面覆盖,即有\(f[l] < g[l], f[r] < g[r]\),那么不需考虑\(f\)向下,返回。
如果\(f[l] > g[l]\),说明\(f\)在左半部分还是有优势,相对在右半部分一定无优势,所以向\([l, mid]\)递归传下\(f\)。
反之,如果\(f[r] > g[r]\),向\([mid+1, r]\)递归传下\(f\)即可。
全局上,在修改时,先找出线段范围对应线段树节点,在从节点开始向下做标记比较。
有\(log_2n\)个节点,递归也是深度即\(log_2n\)级,所以总复杂度为\(O(nlog_2^2n)\)。
模版题代码:P4097 【模板】李超线段树 / [HEOI2013] Segment
细节部分:
注意要求相同纵坐标还要按编号小输出,不妨直接用编号作标记,
同时等于也要考虑下传
数据范围需要long double
。
点击查看代码
#include<algorithm>
#include<stdio.h>
#define ll long long
#define N 100005
#define db long double
using namespace std;
ll read() {
char c=getchar(); ll x=0, y=1;
while(!(c >= '0' && c <= '9' || c == '-')) c=getchar();
if(c == '-') y=-1, c=getchar();
while(c >= '0' && c <= '9') x=x*10+c-'0', c=getchar();
return x*y;
}
int n;
struct line {
db k, b;
} li[N];
db calc(int x, ll pos) {
return li[x].k * pos + li[x].b;
}
struct SMT {
struct node {
int l, r;
int lin;
} t[N<<2];
int ls(int x) {return x<<1;}
int rs(int x) {return x<<1|1;}
void tree_pre(int x, int l, int r) {
t[x].l=l, t[x].r=r;
if(l == r) return;
int mid = (l+r) >> 1;
tree_pre(ls(x), l, mid);
tree_pre(rs(x), mid+1, r);
}
void tagd(int x, int l, int r, int seg) {
int mid = (l+r) >> 1;
int f = seg, g = t[x].lin;
if(calc(f, mid) > calc(g, mid)) swap(f, g);
// printf("%d %d %.2lf %.2lf %.2lf %.2lf\n", l, r, calc(f, l), calc(f, r), calc(g, l), calc(g, r));
if(calc(f, l) < calc(g, l) && calc(f, r) < calc(g, r)) {
t[x].lin = g;
return;
}
if(calc(f, l) >= calc(g, l)) {
t[x].lin = g;
tagd(ls(x), l, mid, f);
}
if(calc(f, r) >= calc(g, r)) {
t[x].lin = g;
tagd(rs(x), mid+1, r, f);
}
}
void modify(int x, int l, int r, int seg) {
if(t[x].l >= l && t[x].r <= r) {
tagd(x, t[x].l, t[x].r, seg);
return;
}
if(t[ls(x)].r >= l) modify(ls(x), l, r, seg);
if(t[rs(x)].l <= r) modify(rs(x), l, r, seg);
}
void query(int x, int des, int &ans, db &num) {
// printf("%.2f %d\n", calc(t[x].lin, des), t[x].lin);
if(calc(t[x].lin, des) > num) {
num = calc(t[x].lin, des);
ans = t[x].lin;
}
else if(calc(t[x].lin, des) == num) {
ans = min(ans, t[x].lin);
}
if(t[x].l == t[x].r) {
return;
}
if(t[ls(x)].r >= des) query(ls(x), des, ans, num);
else query(rs(x), des, ans, num);
}
} T;
db xi[N], yi[N];
const ll M1 = 39989, M2 = 1000000000;
int main() {
// freopen("P4097.in", "r", stdin);
// freopen("P4097.out", "w", stdout);
n=read();
int i;
int lastans=0;
li[0] = (line){0, -1e9};
int n1=0;
int nn = 10;
T.tree_pre(1, 1, 50000);
for(i=1; i<=100000; i++) {
xi[i] = 0;
yi[i] = -1e9;
}
for(i=1; i<=n; i++) {
int op=read();
if(op == 0) {
int k=read();
k = (k+lastans-1)%39989 + 1;
int ans=xi[k];
db num=yi[k];
T.query(1, k, ans, num);
printf("%d\n", ans);
// printf("%.2f\n", num);
lastans = ans;
}
else {
ll x0=read(), y0=read(), x1=read(), y1=read();
x0 = (x0+lastans-1)%M1 + 1;
y0 = (y0+lastans-1)%M2 + 1;
x1 = (x1+lastans-1)%M1 + 1;
y1 = (y1+lastans-1)%M2 + 1;
// printf("%lld %lld %lld %lld\n", x0, y0, x1, y1);
if(x0 > x1) swap(x0, x1), swap(y0, y1);
n1++;
if(x0 == x1) {
if(max(y0, y1) > yi[x0]) yi[x0] = max(y0, y1), xi[x0] = n1;
}
else {
db k = (db)(y1-y0) / (db)(x1-x0);
li[n1] = {k, y0-k*x0};
// printf("%.2f %.2f\n", calc(n1, x0), calc(n1, x1));
T.modify(1, x0, x1, n1);
}
}
}
return 0;
}