李超树——倾斜的线段树

介绍

李超树用以解决一种问题:向平面上插入若干条直线(或线段),查询与直线x=a相交线段的交点坐标y的最大值,

别看这种问题好像挺个别,很多平面题或DP(斜率优化)的题都可以转换成这种问题,而遇到这种问题,李超树比平衡树好打得多。

思想

李超树代码看上去和普通线段树非常像,不同的是,每个节点维护的不是[l,r]的区间,而是两端x坐标为 l、r 的一条带斜率的线段:

(注意,每层的线段高低关系不确定,这样画只是为了好看)

这样查询只需像线段树那样查就是了,

然后怎么插入线段呢?(直线就是 -inf 到 inf 的线段)只需像线段树那样插入,遇到线段与区间重合时,有如下三种情况:

  1. 新线段在旧线段上方直接覆盖即可
  2. 新线段在旧线段下方直接忽略即可
  3. 新旧线段相交,把其中任意一条线段分开,插入到两个儿子中,然后该节点保留另一条线段。这样做的复杂度其实等价于把这两条线段分开,必定有一边相交,另一边不相交,不相交的就是1,2两种情况,O(1)判掉,这样往下传下去就是一个log。

插入线段的复杂度最坏是O(log^2n)(插入直线是O(logn)的,因为只对应一个区间),查询是O(logn),总的复杂度就是O(nlogn)~O(nlog^2n)

板子

代码大概长这样(直线动态开点版):

#define INF 0x7f7f7f7f
#define ll long long
int IN;
struct lcs{
	int ls,rs;ll k,b;lcs(){}
	lcs(ll K,ll B){ls=rs=0,k=K,b=B;}
}t[MAXN<<4];
inline void add(int x,ll l,ll r,ll k,ll b){//插入一条y=kx+b的直线
	if(x==0)return;
	ll tk=t[x].k,tb=t[x].b,mid=(l+r)>>1;
	if(l*k+b>=l*tk+tb&&r*k+b>=r*tk+tb){
		t[x].k=k,t[x].b=b;return;
	}
	else if(l*k+b<l*tk+tb&&r*k+b<r*tk+tb)return;
	else{
		if(!t[x].ls)t[x].ls=++IN,t[IN]=lcs(tk,tb);
		else add(t[x].ls,l,mid,tk,tb);
		if(!t[x].rs)t[x].rs=++IN,t[IN]=lcs(tk,tb);
		else add(t[x].rs,mid+1,r,tk,tb);
		t[x].k=k,t[x].b=b;
	}
}
inline ll sch(int x,ll l,ll r,ll g){//查找x=g处的最高点
	if(x==0)return -INF;
	ll mid=(l+r)>>1,res=g*t[x].k+t[x].b;
	if(g<=mid)res=max(res,sch(t[x].ls,l,mid,g));
	else res=max(res,sch(t[x].rs,mid+1,r,g));
	return res;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值