例题:bzoj1568
那么这就是在二维平面里维护直线,资磁插入一条直线和查询直线x=k与其他直线相交的点中,最大的y坐标。
那么什么是李超线段树呢?
对于一个区间,我们维护该区间的所有直线中,从上往下去看可以看到它的长度最大的一条直线(即没有被其他直线覆盖的长度最大)
现在插入一条直线(称为当前直线)到了一个区间。
1.如果还没有记录直线,则将当前直线变成记录直线,返回。
2.如果当前直线完全被记录直线覆盖,直接返回。
3.如果当前直线完全覆盖记录直线,将当前直线变成记录直线,返回。
4.如果当前直线和记录直线有交点,则求出交点的x坐标,根据x坐标和当前区间的中点的大小等关系,判断当前直线和记录直线中哪个的未被覆盖长度长一些,长一些的叫l1,短一些的叫l2
记录直线变为l1,继续递归处理l2
复杂度证明:由于第4步操作中,我们要递归的区间每次减小了至少一半。所以是 O(logn) O ( l o g n ) 的。注意我们复杂度的关键是让继续递归的区间减小一半,所以假设该题的x坐标要离散化,对于线段树上位置i,实际对应的x是 xi x i ,那么我们应该让交点x坐标和当前区间的 xl+r2 x l + r 2 作比较,而非与 xl+xr2 x l + x r 2 比较。
对于查询操作,我们在线段树上递归到查询的那个x,在过程中,每路过一个区间,都要把这个区间的记录直线拿来更新答案。这是标记永久化。
所以什么是李超线段树?用标记永久化维护平面内线段覆盖情况的线段树
#include<bits/stdc++.h>
using namespace std;
#define RI register int
typedef double db;
typedef long long LL;
const int n=50000,N=50005;
int Q,flag[N<<2];db b[N<<2],k[N<<2];
void insert(int s,int t,int i,db B,db K) {
if(!flag[i]) {b[i]=B,k[i]=K,flag[i]=1;return;}
int mid=(s+t)>>1;
db l1=1.0*s*K+B,r1=1.0*t*K+B,l2=1.0*s*k[i]+b[i],r2=1.0*t*k[i]+b[i];
if(l1<=l2&&r1<=r2) return;
if(l1>l2&&r1>r2) {b[i]=B,k[i]=K;return;}
db xx=(B-b[i])/(k[i]-K);
if(l1>l2) {
if(xx>mid) insert(mid+1,t,(i<<1)|1,b[i],k[i]),b[i]=B,k[i]=K;
else insert(s,mid,i<<1,B,K);
}
else {
if(xx>mid) insert(mid+1,t,(i<<1)|1,B,K);
else insert(s,mid,i<<1,b[i],k[i]),b[i]=B,k[i]=K;
}
}
db query(int x,int s,int t,int i) {
if(s==t) return 1.0*x*k[i]+b[i];
int mid=(s+t)>>1;db re=1.0*x*k[i]+b[i];
if(x<=mid) re=max(re,query(x,s,mid,i<<1));
else re=max(re,query(x,mid+1,t,(i<<1)|1));
return re;
}
int main()
{
char ch[10];int x;db B,K;
scanf("%d",&Q);
while(Q--) {
scanf("%s",ch);
if(ch[0]=='P') scanf("%lf%lf",&B,&K),insert(1,n,1,B-K,K);
else {
scanf("%d",&x);
db ans=query(x,1,n,1);
printf("%lld\n",(LL)(ans/100.0));
}
}
return 0;
}
再看一道例题:bzoj4515
树链剖分+李超线段树
现在插入直线变成插入线段,那么要等线段树区间被插入的线段的x区间完全包含时,再执行那些比较当前区间未被覆盖长度最大的线段的操作,复杂度变成
O(log2n)
O
(
l
o
g
2
n
)
等等…..加上树剖的复杂度,
O(nlog3n)
O
(
n
l
o
g
3
n
)
,
105
10
5
,1s……
所以要相信树剖和李超线段树的常数都是很小的=。=
然后在李超线段树里维护一个
mi
m
i
,表示区间最小值,计算mi的时候,要算线段树上左右区间mi的贡献和当前区间的记录线段的贡献。
#include<bits/stdc++.h>
using namespace std;
#define RI register int
typedef long long LL;
typedef double db;
LL read() {
LL q=0,w=1;char ch=' ';
while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
if(ch=='-') w=-1,ch=getchar();
while(ch>='0'&&ch<='9') q=q*10+(LL)(ch-'0'),ch=getchar();
return q*w;
}
const int N=100005;
const LL inf=123456789123456789;
int n,m,tot,tim;
int h[N],ne[N<<1],to[N<<1],sz[N],top[N],fa[N],dep[N],pos[N],repos[N];
LL w[N<<1],dis[N];
void add(int x,int y,LL z) {to[++tot]=y,ne[tot]=h[x],h[x]=tot,w[tot]=z;}
void dfs1(int x,int las) {
sz[x]=1,dep[x]=dep[las]+1,fa[x]=las;
for(RI i=h[x];i;i=ne[i])
if(to[i]!=las) dis[to[i]]=dis[x]+w[i],dfs1(to[i],x),sz[x]+=sz[to[i]];
}
void dfs2(int x,int las) {
int mx=0,bj=0;pos[x]=++tim,repos[tim]=x;
for(RI i=h[x];i;i=ne[i])
if(to[i]!=las&&sz[to[i]]>mx) mx=sz[to[i]],bj=to[i];
if(!bj) return;
top[bj]=top[x],dfs2(bj,x);
for(RI i=h[x];i;i=ne[i])
if(to[i]!=las&&bj!=to[i]) top[to[i]]=to[i],dfs2(to[i],x);
}
LL k[N<<2],b[N<<2],mi[N<<2];int flag[N<<2];
void build(int s,int t,int i) {
mi[i]=inf;
if(s==t) return;
int mid=(s+t)>>1;
build(s,mid,i<<1),build(mid+1,t,(i<<1)|1);
}
void up(int i) {mi[i]=min(min(mi[i],mi[i<<1]),mi[(i<<1)|1]);}
void insert(int l,int r,int s,int t,int i,LL K,LL B) {
if(l<=s&&t<=r) {
int mid=(s+t)>>1;
LL dl=dis[repos[s]],dr=dis[repos[t]],dm=dis[repos[mid]];
LL l1=K*dl+B,r1=K*dr+B,l2=k[i]*dl+b[i],r2=k[i]*dr+b[i];
if(!flag[i]) {
k[i]=K,b[i]=B,flag[i]=1,mi[i]=min(mi[i],min(l1,r1));
return;
}
if(l1>=l2&&r1>=r2) return;
if(l1<l2&&r1<r2) {
k[i]=K,b[i]=B,mi[i]=min(mi[i],min(l1,r1));
return;
}
db xx=(db)(b[i]-B)/(db)(K-k[i]);
if(l1<l2) {
if(xx<(db)dm) insert(l,r,s,mid,i<<1,K,B);
else insert(l,r,mid+1,t,(i<<1)|1,k[i],b[i]),k[i]=K,b[i]=B;
}
else {
if(xx<(db)dm) insert(l,r,s,mid,i<<1,k[i],b[i]),k[i]=K,b[i]=B;
else insert(l,r,mid+1,t,(i<<1)|1,K,B);
}
mi[i]=min(mi[i],min(l1,r1)),up(i);
return;
}
int mid=(s+t)>>1;
if(l<=mid) insert(l,r,s,mid,i<<1,K,B);
if(mid+1<=r) insert(l,r,mid+1,t,(i<<1)|1,K,B);
up(i);
}
LL query(int l,int r,int s,int t,int i) {
if(l<=s&&t<=r) return mi[i];
int mid=(s+t)>>1;LL re=inf;
if(flag[i]) {
LL dl=dis[repos[max(l,s)]];
LL dr=dis[repos[min(r,t)]];
re=min(k[i]*dl+b[i],k[i]*dr+b[i]);
}
if(l<=mid) re=min(re,query(l,r,s,mid,i<<1));
if(mid+1<=r) re=min(re,query(l,r,mid+1,t,(i<<1)|1));
return re;
}
int lca(int x,int y) {
while(top[x]!=top[y]) {
if(dep[top[x]]<dep[top[y]]) swap(x,y);
x=fa[top[x]];
}
return dep[x]<dep[y]?x:y;
}
void walk(int x,int o,LL K,LL B) {
while(top[x]!=top[o]) {
insert(pos[top[x]],pos[x],1,n,1,K,B);
x=fa[top[x]];
}
insert(pos[o],pos[x],1,n,1,K,B);
}
LL getans(int x,int y) {
LL re=inf;
while(top[x]!=top[y]) {
if(dep[top[x]]<dep[top[y]]) swap(x,y);
re=min(re,query(pos[top[x]],pos[x],1,n,1));
x=fa[top[x]];
}
if(pos[x]>pos[y]) swap(x,y);
re=min(re,query(pos[x],pos[y],1,n,1));
return re;
}
int main()
{
int x,y,bj;LL z,A,B;
n=read(),m=read();
for(RI i=1;i<n;++i)
x=read(),y=read(),z=read(),add(x,y,z),add(y,x,z);
dfs1(1,0),top[1]=1,dfs2(1,0);
build(1,n,1);
while(m--) {
bj=read(),x=read(),y=read();
int o=lca(x,y);
if(bj==1) {
A=read(),B=read();
walk(x,o,-A,A*dis[x]+B);
walk(y,o,A,A*(dis[x]-2LL*dis[o])+B);
}
else printf("%lld\n",getans(x,y));
}
return 0;
}