李超线段树

简介

李超线段树是一种支持添加函数和维护单点最值的数据结构

主要维护方法:懒标记+单侧递归下穿

思路

以P4097 【模板】李超线段树 / [HEOI2013] Segment为例

修改

我们考虑每个节点维护一个懒标记,表示在中点时取值最大的直线

这个懒标记显然是不能代表整个区间的,因此我们需要向下递归

图片

如图,我们现在要加入直线 \(f\),我们先更新懒标记,然后我们再递归

递归时,我们发现左侧是不必要的,因为 \(f\) 和 \(g\) 的交点在右侧,所以左侧的 \(f\) 一定大于 \(g\),我们沿用中点的懒标记就可以了,因此每次递归的规模都会减半,类似于单侧递归,一次修改的复杂度是 \(O(log^2n)\) 的

至于怎么描述交点的位置,我们假设 \(f\) 在中点处更优(更新时swap可以保证),那么如果右端点处 \(g\) 更优,那么交点一定在右侧(如图),反之亦然

查询

查询的时候将遍历过每个节点的懒标记都求一次最值即可,这样一定是正确的,因为一个懒标记代表了一半区间,而另一半会在儿子中算到

代码

#include<bits/stdc++.h>
using namespace std;
#define db double
#define mid ((l+r)>>1)
#define ls (p<<1)
#define rs ((p<<1)|1)
#define lson ls,l,mid
#define rson rs,mid+1,r
#define pdi pair<db,int>
const int mod=39989,mod1=1e9,N=40005,M=1e5+5;
const db eps=1e-9;
struct line{db k,b;}a[M];
int tr[N<<2],tot,n,lst;
int cmp(db x,db y){
	if(x-y>eps) return 1;
	else if(y-x>eps) return -1;
	else return 0;
}
db calc(int p,int d){return a[p].k*d+a[p].b;}
void add(int x0,int x1,int y0,int y1){
	tot++;
	if(x0==x1) a[tot]={0,max(y0,y1)};//特判
	else a[tot].k=1.0*(y0-y1)/(x0-x1),a[tot].b=y0-a[tot].k*x0;
}
void update(int p,int l,int r,int v){
	int& u=tr[p],c=cmp(calc(v,mid),calc(u,mid));
	if(c==1||(!c&&v<u)) swap(u,v);
	if(l==r) return;
	int cl=cmp(calc(v,l),calc(u,l)),cr=cmp(calc(v,r),calc(u,r));
	if(cl==1||(!cl&&v<u)) update(lson,v);
	if(cr==1||(!cr&&v<u)) update(rson,v);
}
void modify(int p,int l,int r,int L,int R,int v){
	if(L<=l&&r<=R){update(p,l,r,v);return;}
	if(L<=mid) modify(lson,L,R,v);
	if(R>mid) modify(rson,L,R,v);
}
pdi pmax(pdi x,pdi y){
	if(!cmp(x.first,y.first)) return x.second<y.second?x:y;
	else return cmp(x.first,y.first)==1?x:y;
	
}
pdi query(int p,int l,int r,int x){
	pdi res={calc(tr[p],x),tr[p]};
	if(l==r) return res;
	if(x<=mid) res=pmax(res,query(lson,x));
	else res=pmax(res,query(rson,x));
	return res;
}
void upd(int& x){x=(x+lst-1+mod)%mod+1;}
void upd1(int& x){x=(x+lst-1+mod1)%mod1+1;}
int main(){
	cin>>n;
	while(n--){
		int op;cin>>op;
		if(op==1){
			int x0,x1,y0,y1;cin>>x0>>y0>>x1>>y1;upd(x0),upd1(y0),upd(x1),upd1(y1);
			if(x0>x1) swap(x0,x1),swap(y0,y1);
			add(x0,x1,y0,y1),modify(1,1,mod,x0,x1,tot);
		}else{
			int k;cin>>k;upd(k);
			cout<<(lst=query(1,1,mod,k).second)<<"\n";
		}
	}
	return 0;
}
posted @ 2026-10-30 22:52  Rainesxiao  阅读(4)  评论(0)    收藏  举报