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

李超线段树

时间:2023-07-05 21:33:47浏览次数:29  
标签:return int 线段 李超 插入 id

引入与概括

思考下列问题:

在平面直角坐标系中维护集合,支持下列操作:

  • 加入一个定义域为 \([l,r]\) 的一次函数。

  • 查询所有定义域包含 \(x\) 的一次函数的函数值的最值。

我们发现,这可以看成一个区间修改,单点查询的问题,考虑使用线段树维护。

但我们发现传统线段树难以维护,于是李超线段树应运而生。

李超线段树常用于 DP 优化(斜率优化),常数小,代码复杂度小。

李超线段树是一种值域线段树,当值域过大无法承受时可以 离散化 / 动态开点。

具体实现

在线段树的每一个节点维护当前区间可能成为最优解的线段(也就是一次函数,二者可以转化),即该线段在区间的某个取值上有最优解。

查询时只需要遍历 \(O(\log n)\) 个节点即可找出最优解,时间复杂度为 \(O(\log n)\),插入直线(定义域包含整个值域的线段)时时间复杂度为 \(O(\log n)\),插入线段时时间复杂度为 \(O(\log^2n)\)。

  • 简单处理(以维护最小值为例)
struct Line{//每一条直线用斜截式 k,b 表示
	int k,b;
}line[N];

int calc(int id,int x){//对应给定线段和横坐标计算纵坐标
	return x*line[id].k+line[id].b;
}

bool Less(int id1,int id2,int x){//比较线段的函数值
	return calc(id1,x)<calc(id2,x);
}
  • 插入

插入直线:

void add(int p,int l,int r,int id){
    if(!a[p]){a[p]=id;return ;}
	if(l==r){if(Less(id,a[p],l)) a[p]=id;return ;}
	if(Less(id,a[p],mid)) swap(a[p],id);
	if(Less(id,a[p],l)) add(p<<1,l,mid,id);
	if(Less(id,a[p],r)) add(p<<1|1,mid+1,r,id);
}

插入线段:

void add(int p,int l,int r,int x,int y,int id){//先找到线段对应的区间再按直线的方式插入
	if(x<=l&&r<=y){
		if(!a[p]){a[p]=id;return ;}
		if(l==r){if(Less(id,a[p],l)) a[p]=id;return ;}
		if(Less(id,a[p],mid)) swap(a[p],id);
		if(Less(id,a[p],l)) add(p<<1,l,mid,x,y,id);
		if(Less(id,a[p],r)) add(p<<1|1,mid+1,r,x,y,id);
		return ;
	}
	if(x<=mid) add(p<<1,l,mid,x,y,id);
	if(y>mid) add(p<<1|1,mid+1,r,x,y,id);
}
  • 查询

查询函数值:

int query(int p,int l,int r,int x){
    int res=calc(a[p],x);
	if(l==r) return res;
	if(x<=mid) res=min(res,query(p<<1,l,mid,x));
	else res=min(res,query(p<<1|1,mid+1,r,x));
	return res;
}

查询函数值对应线段:

int query(int p,int l,int r,int x){
	if(l==r) return a[p];
    int res=0;
	if(x<=mid) res=query(p<<1,l,mid,x);
	else res=query(p<<1|1,mid+1,r,x);
    return Less(a[p],res,x)?a[p]:res;
}

标签:return,int,线段,李超,插入,id
From: https://www.cnblogs.com/TKXZ133/p/17529789.html

相关文章

  • 【线段树】 HDOJ 5274 Dylans loves tree
    用dfs序构建线段树,然后用lca求出两点间路径的xor和。。。#include<iostream>#include<queue>#include<stack>#include<map>#include<set>#include<bitset>#include<cstdio>#include<algorithm>#include<cstring>#include......
  • 线段树分治 学习笔记
    离线算法。在时间轴上建线段树(可能要事先离散化),要维护的东西用vector什么的挂在线段树的节点上,DFS一遍线段树,每次进入一个节点就加入要维护的东西,离开时撤销即可。由于DFS的特性,只需支持最近的undo,用stack可维护。......
  • 线段树区间查改(懒标记+代码细节)
    就如同我上次写链式前向星一样,这次我又一次在模拟赛中打算混点分。经过我缜密的思考基于暴力的猜测,我认为带懒操作的线段树至少可以混70分!(大雾弥漫)。于是我兴冲冲的开始敲代码,然后……线段树就打挂了……比赛结束后我痛定思痛,决定要好好复习一下线段树,然后经过我一下午的折腾,......
  • [Java]线段树
    线段树不含懒标记(单点修改)代码维护区间最大/最小值Node[]tr=newNode[400010];classNode{intl,r,max,min;Node(intl,intr,intmax,intmin){this.l=l;this.r=r;this.max=max;this.min=min;}}vo......
  • 2023ACM暑假训练day 8-9 线段树
    目录DAY8-9线段树训练情况简介题DAY8-9线段树训练地址:传送门训练情况简介题题意:思路:......
  • 李超线段树 学习笔记
    李超线段树学习笔记今天模拟赛用到了李超线段树(但是本蒟蒻费了半天劲搞了个斜率优化拿到了60pts的好成绩/kk),所以学习一下李超线段树刻不容缓(学会了我貌似也切不来那道题qwq)。引入初中和高中我们都做过函数题吧,是不是有时候给你两根甚至几根直线,然后问你某个点的最值?当然,......
  • [数据结构]Segment tree(线段树)
    Segmenttree(线段树)1.线段树的结构和思想线段树基本结构:简单操作:1.单点修改:时间复杂度O(logn),因为线段树高是O(logn)的,然后会修改这个点到根的路径上的点权,所以是O(logn)的。2.区间查询(比如:最小值)实现:#include<bits/stdc++.h>usingnamespacestd;typedeflong......
  • 线段树优化建图 拓扑排序 6.22西安集训T1
    题目链接有一条无限长的数轴,上面有 nn 个坑,第 ii 个坑的位置为 x_ixi​。你将要在数轴上再放置 nn 个球,第 ii 个将要放到的位置为 y_iyi​。每当有一个球被放上去之后,它就会滚落到离它最近的一个坑里并填上那个坑。如果有两个坑都离它最近,那么它会落到左边的里面。现......
  • 「解题报告」P8861 线段
    有趣ds题。首先有一个部分分\(l_i\le10^5\ler_i\)。发现这相当于可以把区间分成左右两部分,那么我们可以考虑将左右分开考虑。我们将每个区间拆开成两部分,这样插入的时候就直接插入即可,修改操作时,发现实际上就是将左端所有长度大于\(10^5-l\)的区间长度改为\(10^5-......
  • 计算几何之两条线段的交点
    1.概述可以通过线段的跨立实验[1]判断两条线段是否相交,但是想要进一步求它们的交点还是比较麻烦。[2]给出的方法更加简单,其原理来自求三维空间两条线段的交点[3]。为了更好的理解,本文将详细介绍二维空间两条线段的交点求解过程。2.两条线段交点求解过程给定两条线段\(\overl......