李超线段树
李超线段树是一种用于维护平面直角坐标系内线段关系的数据结构。它常被用来处理这样一种形式的问题:给定一个平面直角坐标系,支持动态插入一条线段,询问从某一个位置
(
X
,
+
∞
)
(X,+\infty)
(X,+∞) 向下看能看到的最高的一条线段(也就是给一条竖线,问这条竖线与所有线段的最高的交点
如上图,有三条线段,两条红色竖线代表两个询问,则点
A
A
A 与点
B
B
B 就是询问到的答案。
李超线段树的核心是维护每个区间的“最优势线段”,即在每个区间的中点处最高的线段。询问时我们可以对所有包含横坐标为
x
x
x 的位置的区间上的最优势线段计算答案,最后取个
m
a
x
max
max 。
其实这就相当于维护一个记录当前区间最高线段的,不下传标记的线段树。(显然如果我们访问到的区间内只包含询问的横坐标,那么这个区间的最优势线段就是答案线段,所以这样统计包含答案是能保证正确性的)
如上图,对于区间 [ 0 , 8 ] [0,8] [0,8] ,绿色线段是“最优势线段”
对于修改,我们先把线段的值域分割到线段树的区间上,每次访问一个完整的包含在线段值域中的区间时:
- 若当前区间还没有记录最优势线段,则记录最优势线段并返回。
- 若当前区间的最优势线段被插入的线段完全覆盖,则把最优势线段修改为被插入线段并返回。
- 若当前区间的最优势线段把被插入线断完全覆盖,则直接返回。
- 若当前区间最优势线段与被插入线段有交,则先判断哪条线段在当前区间更优,并把更劣的线段下传到交点所在子区间。(交点两边的部分被这两条线段分别控制,而我们已经让在中点更优的那条线段作为区间最优势线段,因此更劣的那条线段只有可能在交点所在子区间超过当前区间的最优势线段)
代码块:
struct Line {
double k, b;//y = kx + b;
int l, r, flag, idx;//线段树的l 和 r 以及是否有线段覆盖 以及插入线段的编号
Line(){}//无参构造函数
//有参构造函数
Line(double k, double b, int l, int r, int flag, int idx) {
this->k = k, this->b = b, this->l = l, this->r = r, this->flag = flag, this->idx = idx;
}
//算在pos位置得y值是多少
double calc(int pos) {return k * pos + b;}//直线里面pos位置的y值
//线段交点的x坐标
int corss(const Line & rhs) const {return floor((b - rhs.b) / (rhs.k - k));}
}seg[maxn << 1];
inline void update(int cutl, int cutr, int rt,Line rhs) {
//如果当前区间以及被目标区间覆盖
if(rhs.l <= cutl && rhs.r >= cutr) {
//如果还没有线段覆盖
if(!seg[rt].flag) {seg[rt] = rhs,seg[rt].flag = 1;}
//如果两端都比原线段高就直接更新
else if(rhs.calc(cutl) - seg[rt].calc(cutl) > eps && rhs.calc(cutr) - seg[rt].calc(cutr) > eps) seg[rt] = rhs;
//如果两线段相交
else if(rhs.calc(cutl) - seg[rt].calc(cutl) > eps || rhs.calc(cutr) - seg[rt].calc(cutr) > eps) {
//取中点
int Mid = (cutl + cutr) >> 1;
//判断两个线段中点的位置的y值大小
//交换
if(rhs.calc(Mid) - seg[rt].calc(Mid) > eps) swap(rhs,seg[rt]);
if(rhs.corss(seg[rt]) - Mid < -eps) update(cutl, Mid, rt << 1, rhs);
else update(Mid + 1, cutr, rt << 1|1,rhs);
}
} else {//线段树常规套路
int Mid = (cutl + cutr) >> 1;
if(rhs.l <= Mid) update(cutl,Mid, rt << 1,rhs);
if(rhs.r > Mid) update(Mid + 1, cutr, rt << 1|1, rhs);
}
}
inline void ask(int rt, int l, int r, int x) {
//每一层都要跑一个询问
double tmp = seg[rt].calc(x);
if(tmp - h > eps) lastans = seg[rt].idx, h = tmp;
else if(fabs(h - tmp) < eps) lastans = min(lastans,seg[rt].idx);
if(l == r) return;
if(x <= mid) ask(Lson,x);
if(x > mid) ask(Rson,x);
}
#include<bits/stdc++.h>
#define mid ((l + r) >> 1)
#define Lson rt << 1, l , mid
#define Rson rt << 1|1, mid + 1, r
#define ms(a,al) memset(a,al,sizeof(a))
#define log2(a) log(a)/log(2)
#define _for(i,a,b) for( int i = (a); i < (b); ++i)
#define _rep(i,a,b) for( int i = (a); i <= (b); ++i)
#define for_(i,a,b) for( int i = (a); i >= (b); -- i)
#define rep_(i,a,b) for( int i = (a); i > (b); -- i)
#define lowbit(x) ((-x) & x)
#define IOS std::ios::sync_with_stdio(0); cin.tie(0); cout.tie(0)
#define INF 0x3f3f3f3f
#define LLF 0x3f3f3f3f3f3f3f3f
#define hash Hash
#define next Next
#define pb push_back
#define f first
#define s second
#define y1 Y
using namespace std;
const int N = 1e7 + 10, modx = 39989, mody = 1e9;
const int maxn = 4e5 + 10;
const long double eps = 0.0;
const int EPS = 500 * 500;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> PII;
typedef pair<ll,ll> PLL;
typedef pair<double,double> PDD;
const int BUF=30000000;
char Buf[BUF],*buf=Buf;
template<typename T> void read(T &a)
{
for(a=0;*buf<48;buf++);
while(*buf>47) a=a*10+ *buf++ -48;
}
template<typename T, typename... Args> void read(T &first, Args& ... args)
{
read(first);
read(args...);
}
int lastans = 0;
double h;
struct Line {
double k, b;
int l, r, flag, idx;
Line(){}
Line(double k, double b, int l, int r, int flag, int idx) {
this->k = k, this->b = b, this->l = l, this->r = r, this->flag = flag, this->idx = idx;
}
double calc(int pos) {return k * pos + b;}//直线里面pos位置的y值
int corss(const Line & rhs) const {return floor((b - rhs.b) / (rhs.k - k));}//线段交点的x坐标
}seg[maxn << 1];
inline void build(int rt, int l, int r) {
seg[rt] = (Line){0.0,0.0,l,r,0,0};
if(l == r) return;
build(Lson);
build(Rson);
}
inline void update(int cutl, int cutr, int rt,Line rhs) {
if(rhs.l <= cutl && rhs.r >= cutr) {
if(!seg[rt].flag) {seg[rt] = rhs,seg[rt].flag = 1;}
else if(rhs.calc(cutl) - seg[rt].calc(cutl) > eps && rhs.calc(cutr) - seg[rt].calc(cutr) > eps) seg[rt] = rhs;
else if(rhs.calc(cutl) - seg[rt].calc(cutl) > eps || rhs.calc(cutr) - seg[rt].calc(cutr) > eps) {
int Mid = (cutl + cutr) >> 1;
if(rhs.calc(Mid) - seg[rt].calc(Mid) > eps) swap(rhs,seg[rt]);
if(rhs.corss(seg[rt]) - Mid < -eps) update(cutl, Mid, rt << 1, rhs);
else update(Mid + 1, cutr, rt << 1|1,rhs);
}
} else {
int Mid = (cutl + cutr) >> 1;
if(rhs.l <= Mid) update(cutl,Mid, rt << 1,rhs);
if(rhs.r > Mid) update(Mid + 1, cutr, rt << 1|1, rhs);
}
}
inline void ask(int rt, int l, int r, int x) {
double tmp = seg[rt].calc(x);
if(tmp - h > eps) lastans = seg[rt].idx, h = tmp;
else if(fabs(h - tmp) < eps) lastans = min(lastans,seg[rt].idx);
if(l == r) return;
if(x <= mid) ask(Lson,x);
if(x > mid) ask(Rson,x);
}
int main() {
IOS;
int T;
cin >> T;
build(1,1,100000);
int Case = 1;
while(T --){
int op;
cin >> op;
if(op == 1) {
int x0, y0, x1, y1;
cin >> x0 >> y0 >> x1 >> y1;
x0 = (x0 + lastans - 1) % modx + 1;
x1 = (x1 + lastans - 1) % modx + 1;
y0 = (y0 + lastans - 1) % mody + 1;
y1 = (y1 + lastans - 1) % mody + 1;
if(x0 == x1) {
Line tmp = (Line){0.0,(double)max(y0,y1),min(x0,x1),max(x0,x1),0,Case};
update(1,100000,0,tmp);
} else {
Line tmp = (Line){(double)(y1 - y0)/(double)(x1 - x0), (double)y0 - (double)(y1 - y0)/(double)(x1 - x0) * (double)x0,min(x0,x1),max(x0,x1),0,Case};
update(1,100000,1,tmp);
}
Case++;
} else {
int k;
cin >> k; h = -INF;
k = (k + lastans - 1) % modx + 1;
ask(1,1,100000,k);
if(h == -INF) lastans = 0;
cout << lastans << "\n";
}
}
return 0;
}