首页 > 其他分享 >李超线段树

李超线段树

时间:2024-10-10 18:10:47浏览次数:8  
标签:return int 线段 李超 mid calc

1 问题

李超线段树是线段树的一种变种,主要用于维护二维平面上的直线信息以及查询操作。它的应用范围很广,只要式子的形式能转化为一次函数就可以考虑使用李超线段树进行求解 / 优化。

具体的,李超线段树支持下面两种操作:

  • 动态在平面中插入一条线段 / 直线。
  • 在平面上询问与一条直线 \(x=k\) 相交的线段 / 直线中,交点纵坐标最大点的坐标 / 编号。

2 过程

李超线段树的实质是在 \(x\) 轴上对单位长度维护线段树,而在线段树上的每一个节点,维护一个该区间的最优线段,其定义如下:

  • 该线段定义域覆盖整个区间。
  • 该线段在区间中点处取值最大。

那么考虑怎样插入的时候维护这个最优线段。设当前遍历到区间 \([l,r]\),原区间最优线段为 \(l_1\),当前要插入 \(l_2\)。

  • 若当前区间无最优线段,或 \(l_2\) 完全在 \(l_1\) 上方,将 \([l,r]\) 的最优线段改为 \(l_2\)。
  • 若 \(l_1\) 完全在 \(l_2\) 上方,则 \([l,r]\) 最优线段不变。
  • 否则,比较二者在 \(mid\) 处的大小,然后向下递归:
    • 此时考虑上面不是最优线段的线段 \(l\),它仍可能成为接下来子区间的最优线段。我们现在需要知道这条线段 \(l\) 和另一条线段在哪个区间相交,就向哪个区间递归。
    • 具体的,我们比较两个线段在两个端点的大小情况,然后以此判断交点位置即可。如果 \(l\) 在左端点处取值更小,又因为 \(l\) 在终点处取值也更小,因此交点必在右区间;否则就应该在左区间。

对于查询,由于我们维护的是每一个区间在 \(mid\) 处取最大值的线段,那么实际上每一个节点维护的信息是类似于没有下传的懒标记的。所以查询时,我们要从查询点对应的叶子节点一路向上走并取最大值。这实际上是标记永久化的思想。

如果插入的是线段,那么单次查询复杂度为 \(O(\log n)\),而单次插入复杂度为 \(O(\log^2 n)\)。如果插入的是直线,查询复杂度不变,单次插入复杂度变为 \(O(\log n)\)。

同时值得注意的是,由于李超线段树常常维护的是值域上的信息,因此会需要用动态开点,而李超线段树动态开点的空间复杂度是 \(O(m)\) 的,其中 \(m\) 为线段个数。

3 实现

(下列代码以动态开点形式实现)

对于插入直线的查询操作:

void mdf(int &p, int l, int r, Seg g) {
    if(!p) {//该区间没有最优线段
        p = ++tot;
        t[p].s = g;//直接更新
        return ;
    }
    int mid = (l + r) >> 1;
    if(g.calc(mid) > t[p].s.calc(mid)) swap(g, t[p].s);
    //比较终点处的值,让 t[p].s 中存的是最优线段
    if(l == r) {//递归到叶子节点了
        return ;
    }
    if(t[p].s.calc(l) > g.calc(l)) mdf(rp, mid + 1, r, g);
    //如果左端点最优线段取值更大,则说明交点在右端点
    else if(t[p].s.calc(l) < g.calc(l)) mdf(lp, l, mid, g);
    //否则在左端点
}

对于插入线段的操作,先 \(O(\log n)\) 的将线段拆成几段,然后对于每一段再用与上面方法相同的插入方式处理即可。这也解释了为什么插入线段复杂度为 \(O(\log^2 n)\)。

对于查询 \(x=k\) 直线交点最大值:

int query(int p, int l, int r, int x) {
    if(!p) return -Inf;
    if(l == r) {
        return t[p].s.calc(x);
    }
    int mid = (l + r) >> 1;
    if(x <= mid) return max(t[p].s.calc(x), query(lp, l, mid, x));
    else return max(t[p].s.calc(x), query(rp, mid + 1, r, x));//标记永久化的处理方式
}

整体代码实现:

struct Seg {//封装直线
	int k, b;
	int calc(int x) {
		return k * x + b;
	}
};

int rt;
struct LC_SegTree {//封装李超线段树
	struct node {
		int l, r;
		Seg s;
	}t[Maxn];
	#define lp t[p].l
	#define rp t[p].r
	int tot;
	void mdf(int &p, int l, int r, Seg g) {
		if(!p) {
			p = ++tot;
			t[p].s = g;
			return ;
		}
		int mid = (l + r) >> 1;
		if(g.calc(mid) > t[p].s.calc(mid)) swap(g, t[p].s);
		if(l == r) {
			return ;
		}
		if(t[p].s.calc(l) > g.calc(l)) mdf(rp, mid + 1, r, g);
		else if(t[p].s.calc(l) < g.calc(l)) mdf(lp, l, mid, g);
	}
	int query(int p, int l, int r, int x) {
		if(!p) return -Inf;
		if(l == r) {
			return t[p].s.calc(x);
		}
		int mid = (l + r) >> 1;
		if(x <= mid) return max(t[p].s.calc(x), query(lp, l, mid, x));
		else return max(t[p].s.calc(x), query(rp, mid + 1, r, x));
	}
}seg;

标签:return,int,线段,李超,mid,calc
From: https://www.cnblogs.com/dzbblog/p/18456889

相关文章

  • 李超线段树
    最经典应用就是维护一个二维平面直角坐标系,支持动态插入线段(理解为有值域的一次函数\(y=kx+b\)),询问已插入线段与直线\(x=x_0\)交点的纵坐标最值。即当\(x=x_0\),求\(max\)或\(min\){\(k_ix+b_i\)}对于普通求法的\(O(n)\)遍历,如何优化时间复杂度呢?其实就是找一种方......
  • B. 线段取交
    题目下载链接算法可以发现是求逆序对时间复杂度限制在\(O(n\logn)\)树状数组记录每一个值的多少转化为求前缀和#include<iostream>#include<cstdio>#include<algorithm>usingnamespacestd;inttree[500010],ranks[500010],n;longlongans;structpoint{......
  • 2024.10.1 总结(集训;数据结构 主要是线段树)
    XK又来给我们讲课了。开心!1.Baka'sTrick两种理解:双栈模拟队列。[找到若干个划分点,使得每个区间包含恰好一个划分点。维护划分点到划分点段的前缀、后缀信息。在在线的实现中,在队列中维护仅仅一个划分点,维护它到前面每个点和它到后面每个点的信息。当这个划分点出队时,把队......
  • 力扣(leetcode)每日一题 1845 座位预约管理系统| treeSet和priority Queue的区别|线段树
    之前发过一篇,感觉还有深挖的地方,于是又补充一些信息这题目虽然是middle难度题目,要解答出来是只要easy的时间,但是深挖可以有hard的难度题解1可以帮助复习线段树的使用,题解2可以复习一下java基础知识题解1线段树这是自己憋出来的线段树classSeatManager{......
  • Interval GCD(单点修改线段树)
    细节不少//根据更相减损法gcd(x,y)=gcd(x,y-x)//推广到三项为gcd(x,y,z)=gcd(x,y-x,z-y)//可以推广到n项#include<bits/stdc++.h>usingnamespacestd;#definexfirst#defineysecondtypedefpair<int,int>PII;typedeflonglongll;typedefunsignedlonglong......
  • Can you answer these queries III(单点修改线段树)
    因为洛谷出现UE在acwing提交,输入格式略有修改#include<bits/stdc++.h>usingnamespacestd;#definexfirst#defineysecondtypedefpair<int,int>PII;typedeflonglongll;typedefunsignedlonglongull;typedefvector<string>VS;typedefvector<int>......
  • P3372 【模板】线段树 1
    注意size信息应该存放在info里和tag运算,已经tag是表示子树未处理的信息#include<bits/stdc++.h>usingnamespacestd;#definexfirst#defineysecondtypedefpair<int,int>PII;typedeflonglongll;typedefunsignedlonglongull;typedefunsignedintuint;typede......
  • WPF 基础 2D 图形学知识 判断点是否在线段上
    在知道一个使用两个点表示的线段,和另一个点,求另一个点是否在线段上本文算法属于通用的算法,可以在WPF和UWP和Xamarin等上运行,基本上所有的.NET平台都能执行如下图,如果点在线段上,那么修改线段颜色假定有线段的定义如下publicrecordLine{publicPo......
  • (可持久化)权值线段树
    权值线段树就是把类型存在线段树上,每个下标存的是类型的数量。可以用来做离线的平衡树,如果值域范围小的话可以在线,只有一只\(\log\)。平衡树六种操作:插入\(x\)就是把\(x\)上的值加\(1\)。modify(1,1,n,x,1);删除一个\(x\)就是把\(x\)上的值加\(-1\)。modify(1,......
  • (nice!!!)LeetCode 2286. 以组为单位订音乐会的门票(线段树)
    题目:2286.以组为单位订音乐会的门票思路:线段树做法。(线段树)acwing1265.数星星classBookMyShow{public: //结构体typedefstructNode{intmn=0;//最小空位编号longlongsum=0;//非空位置之和}node; //n,mintN,M;......