文章目录
李超线段树
介绍
李超线段树作为一种高级数据结构,其最经典的应用就是维护二维平面直角坐标系中,支持动态插入线段,(不支持删除线段),询问已插入直线中与直线 x = x 0 x=x_0 x=x0 相交 y y y 值最大/最小值。其中插入直线复杂度 O ( n l o g n ) O(nlogn) O(nlogn),在指定区间插入线段复杂度 O ( n l o g 2 n ) O(nlog^2n) O(nlog2n)。
主要思想
对于上述问题,暴力做法显然就是 O ( n ) O(n) O(n) 的遍历所有已插入线段,比较交点的 y y y 值。这个做法复杂度的瓶颈在于我们的“最优解“集合过大,实际上有些线段根本不可能成为最优解,那么李超线段树正是运用这种缩小最优解集合的方法降低复杂度。
首先,李超线段树中每个区间只记录在当前区间可能成为最优解的线段,即该线段在当前区间的某个取值上有最优解,我们称这样的线段为优势线段。如何维护区间内的优势线段就成了李超数的核心问题,我们将使用线段树的框架并且利用标记永久化这一方法,接着来具体分析这一问题:
我们以求最大值为例进行分析:
1.当区间内没有任何线段时:我们直接将线段放入当前区间即可
2.当区间内存在旧线段,且新线段的斜率大于旧线段的斜率时,设当前区间中点为 m i d mid mid:
- 如果新线段在中点的值大于旧线段,那么新线段在 [ m i d + 1 , R ] [mid+1,R] [mid+1,R] 上整体取值都比旧线段更优,我们直接用新线段替换旧线段,并且此时旧线段仍可能在 [ L , m i d ] [L,mid] [L,mid] 区间有优势,我们把旧线段下放至 [ L , m i d ] [L,mid] [L,mid] 区间即可
- 如果新线段在中点的值小于旧线段,那么旧线段在 [ L , m i d ] [L,mid] [L,mid] 上整体取值一定都比旧线段更优, 而在 [ m i d + 1 , R ] [mid+1,R] [mid+1,R] 区间新线段仍可能存在优势,所以旧线段仍然是 [ L , R ] [L,R] [L,R] 的优势线段,所以我们保留旧线段,下放线段至 [ m i d + 1 , R ] [mid+1,R] [mid+1,R]
3.当区间内存在旧线段,且新线段的斜率小于旧线段的斜率时同上
4.当区间内存在旧线段,且新线段的斜率相同时,显然我们只需要保留截距更大的线段即可
5.上述过程已经可以很好的维护优势线段,但是在此之前我们可以先判断两条线段是否存在严格优势关系,如果存在我们可以直接排除另一条线段,这样我们在情况 2,3 时就可以保证下放的线段不是毫无用处的,相当于做一个小剪枝
可以发现上述过程中我们维护的所谓优势线段,并不是严格的,复杂度因此有了保障。但是我们在查询的时候显然不能只递归到一个叶子节点,我们必须在向下递归的同时计算经过的线段的最值,即利用标记永久化来得到最优解。
实现
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e6+10;
const int maxm=2e5+10;
const int p=998244353;
int k[maxm],b[maxm];
int tag0[maxn<<2],tag1[maxn<<2];
int n,m,tot=0;
inline ll cal(int i,int x){
return 1ll*k[i]*x+b[i];
}
void modify_min(int l,int r,int x,int i){
if(!tag0[x]){
tag0[x]=i;return;}
if(l==r){
if(cal(tag0[x],l)>cal(i,l)) tag0[x]=i;
return;
}
int mid=l+r>>1;
ll y1=cal(tag0[x],mid),y2=cal(i,mid);
if(k[tag0[x]]>k[i]){
if(y1<=y2) modify_min(mid+1,r,x<<1|1,i);
else modify_min(l,mid,x<<1,tag0[x]),tag0[x]=i;
}
else if(k[tag0[x]]<k[i]){
if(y1<=y2) modify_min(l,mid,x<<1,i);
else modify_min(mid+1,r,x<<1|1,tag0[x]),tag0[x]=i;
}
else if(b[tag0[x]]>b[i]) tag0[x]=i;
}
void modify_max(int l,int r,int x,int i){
if(!tag1[x]){
tag1[x]=i;return;}
if(l==r){
if(cal(tag1[x],l)<cal(i,l)) tag1[x]=i;
return;
}
int mid=l+r>>1;
ll y1=cal(tag1[x],mid),y2=cal(i,mid);
if(k[tag1[x]]>k[i]){
if(y1<=y2) modify_max(mid+1,r,x<<1|1,tag1[x]),tag1[x]=i;
else modify_max(l,mid,x<<1,i);
}
else if(k[tag1[x]]<k[i]){
if(y1<=y2) modify_max(l,mid,x<<1,tag1[x]),tag1[x]=i;
else modify_max(mid+1,r,x<<1|1,i);
}
else if(b[tag1[x]]<b[i]) tag1[x]=i;
}
ll query_min(int l,int r,int x,int pos){
if(tag0[x]==0) return 1e18;
if(l==r) return cal(tag0[x],pos);
int mid=l+r>>1;
ll ans=cal(tag0[x],pos);
if(pos<=mid) return min(ans,query_min(l,mid,x<<1,pos));
else return min(ans,query_min(mid+1,r,x<<1|1,pos));
}
ll query_max(int l,int r,int x,int pos){
if(tag1[x]==0) return -1e18;
if(l==r) return cal(tag1[x],pos);
int mid=