浅谈李超线段树

众所周知,\(Li\ Chao\ Tree=LCT=Link\ Cut\ Tree\)


在我们的日常学习生活中,经常会遇到以下问题:

维护一种数据结构,要求:

  1. 添加一条线段
  2. 求解 \(x=k\) 与所有线段交点中,\(y\) 最大的一个。

众所周知,线段会影响一个区间的答案。区间取 \(max+\) 单点最大值,想到线段树。

但是该怎么修改哩?

我们设 \(dt_{l,r}\) 表示完全覆盖 \([l,r]\) 的所有线段中,与 \(x=mid\) 的交点 \(y\) 值最大的那条线段的编号。

那么发现如下性质:

  1. 假如新线段与老线段相比,\(x=l\)\(x=r\) 时都更优,那么新线段在整个区间中都完爆老线段。
  2. 假如新线段与老线段相比,\(x=l\)\(x=r\) 时都更劣,那么老线段在整个区间中都完爆新线段。

这两种情况直接改变 \(dt_{l,r}\) 的值就可以了,假如用标记永久化就可以 \(return\) 掉了。

发现假如不符合上述性质,且 \(x=mid\) 时新线段优于老线段,那么可以理解为原来是新线段,加入老线段,直接交换即可。

那么我们现在考虑的问题就是:新线段有可能在哪一段比原先更优。

发现假如 \(x=l\) 时新线段更优,那么只有在 \([l,mid]\) 里,新线段才可能更优,反之亦然。这个手模很好理解。

那么用线段树实现这个过程就好了。

分析时间复杂度:

由于对 \(dt\) 的定义,导致我们必须找到所有被新线段包含区间(数量级为 \(O(\log n)\)),再进行类似单点修改的操作,所以时间复杂度轻轻松松 \(O(n\log^2n)\)

特别的,当插入的是直线时,第一步的数量级变为 \(O(1)\),所以时间复杂度为 \(O(n\log n)\)

#include<bits/stdc++.h>
#define int long long
#define $ inline
using namespace std;
const int N=2e5+5,q=39989,p=1e9;
const double eps=1e-8;
int n,la=-1,id;
double k[N],b[N];
int dcmp(double x){
	return fabs(x)<=eps?0:x<0?-1:1;
}double f(int x,int y){
	return k[x]*y+b[x];
}int ju(int x,int y,int p){
	double fx=f(x,p),fy=f(y,p);
	return dcmp(fx-fy)?fx<fy:x>y;
}struct Lichao{
	int dt[N];
	$ void upd(int x,int l,int r,int L,int R,int v){
		if(L<=l&&r<=R){
			if(ju(v,dt[x],l)&&ju(v,dt[x],r)) return;
			if(ju(dt[x],v,l)&&ju(dt[x],v,r)) return dt[x]=v,void();
			int mid=(l+r)>>1;
			if(ju(dt[x],v,mid)) swap(dt[x],v);
			if(ju(dt[x],v,l)) upd(x<<1,l,mid,L,R,v);
			else upd(x<<1|1,mid+1,r,L,R,v);
			return;
		}int mid=(l+r)>>1;
		if(L<=mid) upd(x<<1,l,mid,L,R,v);
		if(R>mid) upd(x<<1|1,mid+1,r,L,R,v);
	}$ int que(int x,int l,int r,int v){
		if(l==r) return dt[x];
		int mid=(l+r)>>1,re=0;
		if(v<=mid) re=que(x<<1,l,mid,v);
		else re=que(x<<1|1,mid+1,r,v);
		return (!ju(re,dt[x],v))?re:dt[x]; 
	}
}lcst;//Link Cut Tree=Li Chao Tree?
void t(int &x,int qp){
	x=(x+la+qp)%qp+1;
}void solve0(){
	int xa,ya,xb,yb;
	cin>>xa>>ya>>xb>>yb;
	t(xa,q),t(xb,q),t(ya,p),t(yb,p);
	if(xb<xa) swap(xa,xb),swap(ya,yb);
	if(xa==xb) k[++id]=0,b[id]=max(ya,yb);
	else k[++id]=1.0*(yb-ya)/(xb-xa),b[id]=yb-k[id]*xb;
	lcst.upd(1,1,4e4,xa,xb,id);
}void solve1(){
	int k;cin>>k,t(k,q);
	la=lcst.que(1,1,4e4,k);
	cout<<la<<"\n",la--;
}signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	cin>>n;
	for(int i=1;i<=n;i++){
		int opt;cin>>opt;
		if(!opt) solve1();
		else solve0();
	}return 0;
}//基本逻辑:寻找区间中点最优解
posted @ 2024-10-11 21:27  长安一片月_22  阅读(7)  评论(0编辑  收藏  举报