【瞎口胡】李超线段树
现在有如下问题:
要求在平面直角坐标系下在线维护两个操作:
- 在平面上加入一个定义域为 \([l,r]\) 的一次函数。记第 \(i\) 个被插入的一次函数标号为 \(i\)。
- 给定一个数 \(k\),询问与直线 \(x = k\) 相交的一次函数中,交点纵坐标最大的一次函数的编号。
如果插入的一次函数横坐标为整数,那么李超树可以用线段树的分治结构在 \(O(n \log n)\) 的时间复杂度和 \(O(n)\) 的空间复杂度内解决上面的问题。
概述
李超树使用了线段树的结构,按横坐标进行分治。区间 \([l,r]\) 对应节点维护的是 \(x=l\) 到 \(x=r\) 这一段区间的优势函数。要求:
- 该函数覆盖区间 \([l,r]\)。即:和 \(x=l\) 和 \(x=r\) 都有交。
- 该函数在区间中点 \(mid\) 处的取值最大。
那么插入一个函数,会有如下几种情况发生:
- 如果当前函数定义域没有覆盖 \([l,r]\),那么往下递归;
- 否则,如果当前函数更优,更新当前区间的优势函数。此时被淘汰掉的函数(称为劣势函数)仍然有可能成为子区间的优势函数。
- 如果劣势函数在 \(x=l\) 处取值比优势函数大,说明这两个函数在 \([l,mid]\) 处有交,而在 \([mid,r]\) 中,劣势函数取值严格不比优势函数大,所以不可能成为右半区间的优势函数,而左半区间有机会。此时递归左半区间。
- 同理,如果劣势函数在 \(x=r\) 处取比优势函数大,递归右半区间。
查询的时候,需要比较经过的所有节点的优势函数,因为上述更新过程类似于标记永久化。
例题 [HEOI2013]Segment
题意
同上,只不过不保证插入的是一个一次函数(可能会平行于坐标轴)。
操作次数 \(n\) 满足 \(1 \leq n\leq 10^5\),横纵坐标范围分别为 \([1,39989],[1,10^9]\)
题解
注意到平行于 \(x\) 轴不影响,平行于 \(y\) 轴的线段可以勉强写成 \(y=0x+\max\{y_0,y_1\}\) 这样的形式。
直接做就可以了。
# include <bits/stdc++.h>
const int N=40010,INF=0x3f3f3f3f,MAXN=N-10,BMAX=1e9;
const double EPS=1e-8;
struct Seg{
double k,b;
inline double fx(double x){
return k*x+b;
}
}a[100010];
int n,tree[N<<2],lst,cnt; // tree[x] 存节点 x 对应的优势函数编号
inline int read(void){
int res,f=1;
char c;
while((c=getchar())<'0'||c>'9')
if(c=='-')f=-1;
res=c-48;
while((c=getchar())>='0'&&c<='9')
res=res*10+c-48;
return res*f;
}
inline int Sign(double x){
return (x>EPS)?1:((fabs(x)<EPS)?0:-1);
}
inline int lc(int x){
return x<<1;
}
inline int rc(int x){
return x<<1|1;
}
void modify(int k,int l,int r,int L,int R,int q){
if(L<=l&&r<=R){
int mid=(l+r)>>1;
int &p=tree[k];
if(Sign(a[q].fx(mid)-a[p].fx(mid))==1) std::swap(p,q);
if(Sign(a[q].fx(l)-a[p].fx(l))==1) modify(lc(k),l,mid,L,R,q);
if(Sign(a[q].fx(r)-a[p].fx(r))==1) modify(rc(k),mid+1,r,L,R,q);
return;
}
int mid=(l+r)>>1;
if(L<=mid) modify(lc(k),l,mid,L,R,q);
if(mid<R) modify(rc(k),mid+1,r,L,R,q);
return;
}
int query(int k,int l,int r,int x){
if(l==r) return tree[k];
int mid=(l+r)>>1;
int res=((x<=mid)?query(lc(k),l,mid,x):query(rc(k),mid+1,r,x)),sig=Sign(a[res].fx(x)-a[tree[k]].fx(x));
if(!sig) return std::min(res,tree[k]);
return (sig==1)?res:tree[k];
}
inline void trans(int &x,int MOD){
x=(x+lst-1)%MOD+1;
return;
}
int main(void){
n=read();
int op,k,xa,xb,ya,yb;
while(n--){
op=read();
if(!op){
k=read(),trans(k,39989);
printf("%d\n",lst=query(1,1,MAXN,k));
}else{
xa=read(),ya=read(),xb=read(),yb=read();
trans(xa,39989),trans(xb,39989),trans(ya,BMAX),trans(yb,BMAX); // 强制在线
if(xa>xb) std::swap(xa,xb),std::swap(ya,yb);
if(xa==xb) a[++cnt].k=0,a[cnt].b=std::max(ya,yb); // 特判
else a[++cnt].k=(double)(ya-yb)/(double)(xa-xb),a[cnt].b=ya-xa*a[cnt].k;
modify(1,1,MAXN,xa,xb,cnt);
}
}
return 0;
}