算法介绍:
李超线段树是一种用于维护平面直角坐标系内线段(直线)关系的数据结构。
它常被用来处理这样一种形式的问题:
给定一个平面直角坐标系,支持动态插入一条线段(直线),询问从某一个位置横坐标x从上向下能看到的最高的
一条线段(也就是给一条竖线,问这条竖线与所有线段的最高的交点。)
主要思想:
对整个横坐标建线段树,每个节点的值存的是一条直线的编号。
要求这条直线在横坐标为mid的时候纵坐标最大。
插入:
如果是叶子节点,我们直接保留最大的。
先比较斜率,再比较横坐标为mid时的纵坐标。
如果新线段的斜率大,并且横坐标为mid时的纵坐标大,那么把原线段下放左区间,节点值改为x
如果新线段的斜率大,但是横坐标为mid时的纵坐标小,那么把新线段下放右区间,不更改节点值
如果新线段的斜率小,但是横坐标为mid时的纵坐标大,那么把原线段下放右区间,节点值改为x
如果新线段的斜率小,并且横坐标为mid时的纵坐标小,那么把新线段下放左区间,不更改节点值
double f(int w,int x) { return k[w]*(x-1)+b[w]; } void change(int nod,int l,int r,int x) { if(l==r) { if(f(x,l)>f(tree[nod],l))tree[nod]=x; return; } int mid=(l+r)/2; if(k[tree[nod]]<k[x]) { if(f(x,mid)>f(tree[nod],mid)) { change(nod*2,l,mid,tree[nod]); tree[nod]=x; } else change(nod*2+1,mid+1,r,x); } if(k[tree[nod]]>k[x]) { if(f(x,mid)>f(tree[nod],mid)) { change(nod*2+1,mid+1,r,tree[nod]); tree[nod]=x ; } else change(nod*2,l,mid,x) ; } }
查询:
如果是叶子节点直接返回横坐标的值。
如果查询区间在左区间,返回当前的节点的横坐标的值 和 左区间的值 的最大值
如果查询区间在右区间,返回当前的节点的横坐标的值 和 右区间的值 的最大值
double query(int nod,int l,int r,int x) { if(l==r)return f(tree[nod],x); int mid=(l+r)/2; if(x<=mid)return max(f(tree[nod],x),query(nod*2,l,mid,x)); else return max(f(tree[nod],x),query(nod*2+1,mid+1,r,x)); }
例题:
1.P4254 [JSOI2008]Blue Mary开公司
题意:
有两种操作:
1.给你一条一次函数
2.给你一个x,让你求所有函数中最大的y
现在有n个操作,要求你对每一个操作2 输出最大的y值/100的结果
题解:
这是李超线段树裸题,套用上面写的板子就能A了。
只要插入和查询两个操作即可。
代码:
#include using namespace std; const int N=500005; /*char buf[1<<21],*p1=buf,*p2=buf; inline int gc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++;}*/ #define gc getchar inline int read() { int ret=0,f=0;char c=gc(); while(!isdigit(c)){if(c=='-')f=1;c=gc();} while(isdigit(c)){ret=ret*10+c-48;c=gc();} if(f)return -ret;return ret; } int n,m,tree[N*4]; double k[N*2],b[N*2]; char s[20]; double f(int w,int x) { return k[w]*(x-1)+b[w]; } void change(int nod,int l,int r,int x) { if(l==r) { if(f(x,l)>f(tree[nod],l))tree[nod]=x; return; } int mid=(l+r)/2; if(k[tree[nod]]<k[x]) { if(f(x,mid)>f(tree[nod],mid)) { change(nod*2,l,mid,tree[nod]); tree[nod]=x; } else change(nod*2+1,mid+1,r,x); } if(k[tree[nod]]>k[x]) { if(f(x,mid)>f(tree[nod],mid)) { change(nod*2+1,mid+1,r,tree[nod]); tree[nod]=x ; } else change(nod*2,l,mid,x) ; } } double query(int nod,int l,int r,int x) { if(l==r)return f(tree[nod],x); int mid=(l+r)/2; if(x<=mid)return max(f(tree[nod],x),query(nod*2,l,mid,x)); else return max(f(tree[nod],x),query(nod*2+1,mid+1,r,x)); } int main() { n=read(); while(n--) { scanf("%s",s); if(s[0]=='P') { m++; scanf("%lf%lf",&b[m],&k[m]); change(1,1,N,m); } else{ int x=read(); printf("%d\n",(int)(query(1,1,N,x)/100)); } } return 0; }
2.P4097 [HEOI2013]Segment
题目:
要求在平面直角坐标系下维护两个操作:
1. 在平面上加入一条线段。记第 i 条被插入的线段的标号为 i
2. 给定一个数 k,询问与直线 x = k 相交的线段中,交点最靠上的线段的编号。
题解;
这题和上一题的区别就是,这题是线段,上一题是直线。
线段要复杂很多,要把线段割成一段一段的。
修改还时比较是y轴的坐标。
#include using namespace std; #define mid (l+r)/2 const int N=100005; int n,m,lastans; struct node{ int l,r,id; double yl,yr; void Node(int x=0,int y=0,int xx=0,int yy=0,int i=0) { l=x,r=xx,yl=y,yr=yy,id=i; if(l==r)yl=yr=max(yl,yr); } double k(){return (yr-yl)/(r-l);}//斜率 double get(int x){return l==r?yl:yl+(k()*(x-l));}//于直线x的交点 void lm(int x){yl=get(x);l=x;} void rm(int x){yr=get(x);r=x;} }tree[N*4]; /*char buf[1<<21],*p1=buf,*p2=buf; inline int gc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++;}*/ #define gc getchar inline int read() { int ret=0,f=0;char c=gc(); while(!isdigit(c)){if(c=='-')f=1;c=gc();} while(isdigit(c)){ret=ret*10+c-48;c=gc();} if(f)return -ret;return ret; } bool bj(node a,node b,int x) { return a.get(x)==b.get(x)?a.idb.get(x); } void build(int nod,int l,int r) { tree[nod].l=l,tree[nod].r=r; if(l==r)return; build(nod*2,l,mid); build(nod*2+1,mid+1,r); } void change(int nod,int l,int r,node k) { if(tree[nod].l>k.l)k.lm(tree[nod].l); if(tree[nod].r<k.r)k.rm(tree[nod].r); if(bj(k,tree[nod],mid))swap(tree[nod],k); if(min(tree[nod].yl,tree[nod].yr)>=max(k.yl,k.yr))return;//没有交点 if(l==r)return; if(tree[nod].k()<=k.k())change(nod*2+1,mid+1,r,k); else change(nod*2,l,mid,k); } void insert(int nod,int l,int r,node k) { if(k.l>r||k.r<l)return; if(tree[nod].l>k.l)k.lm(tree[nod].l); if(tree[nod].r<k.r)k.rm(tree[nod].r); if(l==k.l&&r==k.r) { change(nod,l,r,k); return; } if(l==r)return; insert(nod*2,l,mid,k); insert(nod*2+1,mid+1,r,k); } node query(int nod,int l,int r,int x) { if(l==r)return tree[nod]; node xu; if(x<=mid)xu=query(nod*2,l,mid,x); else xu=query(nod*2+1,mid+1,r,x); return bj(xu,tree[nod],x)?xu:tree[nod]; } int main() { n=read(); build(1,1,40000); while(n--) { int opt=read(); if(opt==1) { int x=read(),y=read(),xx=read(),yy=read(); x=(x+lastans-1)%39989+1; y=(y+lastans-1)%1000000000 +1; xx=(xx+lastans-1)%39989+1; yy=(yy+lastans-1)%1000000000 +1; if(x>xx)swap(x,xx),swap(y,yy); node xu; xu.Node(x,y,xx,yy,++m); insert(1,1,40000,xu); } else{ int x=read(); x=(x+lastans-1)%39989+1; lastans=query(1,1,40000,x).id; printf("%d\n",lastans); } } return 0; }