浅谈李超线段树
1.$CDQ$ 分治总结2.基环树算法总结3.(抄自己luogu上的博客)莫队总结4.珂朵莉树总结5.虚树总结6.从龟速乘到 $Miller-Rabin$ 算法(数论算法总结)
7.浅谈李超线段树
8.浅谈长链剖分9.浅谈平衡树10.可持久化数据结构算法总结11.线段树运用进阶12.群论学习笔记13.线性基学习笔记14.LCT 学习笔记15.原根学习笔记+BSGS复习笔记16.多项式算法初探:从 FFT 到 NTT17.下降幂、斯特林数学习笔记18.多项式算法再探:FMT 和 FWT19.min-max 容斥(最值反演)学习笔记20.自适应 Simpson 积分法学习笔记21.SAM 学习笔记22.min_25 筛 学习笔记众所周知,\(Li\ Chao\ Tree=LCT=Link\ Cut\ Tree\)。
在我们的日常学习生活中,经常会遇到以下问题:
维护一种数据结构,要求:
- 添加一条线段
- 求解 \(x=k\) 与所有线段交点中,\(y\) 最大的一个。
众所周知,线段会影响一个区间的答案。区间取 \(max+\) 单点最大值,想到线段树。
但是该怎么修改哩?
我们设 \(dt_{l,r}\) 表示完全覆盖 \([l,r]\) 的所有线段中,与 \(x=mid\) 的交点 \(y\) 值最大的那条线段的编号。
那么发现如下性质:
- 假如新线段与老线段相比,\(x=l\) 和 \(x=r\) 时都更优,那么新线段在整个区间中都完爆老线段。
- 假如新线段与老线段相比,\(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;
}//基本逻辑:寻找区间中点最优解
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· .NET10 - 预览版1新功能体验(一)