您的位置:首页 > 理论基础 > 数据结构算法

李超线段树

2017-07-28 21:44 134 查看
现在要求你在线动态维护一个二维平面直角坐标系,支持插入一条线段,询问与直线x=x0相交的所有线段中交点y的最大/最小值

--李超线段树解决的问题

现在我们只考虑询问最大,事实上最小是同理的

线段树维护覆盖这个区间的"最优势线段","最优势线段"指覆盖此区间且暴露最多的线段

比如

对于区间A红色为"最优势线段"

可以证明对于一个位置的询问,答案一定在所有包含这个位置的区间的"最优势线段"里

维护的话,考虑当前加入线段与原"最优势线段"的关系

还没有"最优势线段" -> 直接改

新的把旧的盖住了 -> 直接改

旧的把新的盖住了 -> 直接返回

有交点 -> 比较谁更优势,把不优势的下放到交点所在儿子区间上

询问的话自底向上把每个经过的区间的答案取最大值即可

一条线段会没分配到O(logn)个区间上,每个区间至多下放O(logn)次

修改操作复杂度O(log^2n),查询O(logn)

代码很好写...

例题:

Easy : BZOJ3165 BZOJ1568 都是裸题

//BZOJ3165
#include<bits/stdc++.h>
#define debug(x) cerr<<#x<<"="<<x<<endl
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
using namespace std;
const double eps = 1e-12;
const int maxn = 100009;

struct line
{
int l,r;double k,b;
line(int x0=0,int x1=0,int y0=0,int y1=0)
{
l=x0;r=x1;
if(x0!=x1)
{
k=1.0*(y1-y0)/(x1-x0);
b=y0-k*x0;
}
else k=0,b=max(y0,y1);
}
double f(int x){return x*k+b;}
}a[maxn];
struct sg_tree{int idx;}node[maxn<<2];
int poi[maxn];
int n,tot,mr=39989,mod=1e9,last;

inline int sgn(double x){return (x>-eps)-(x<eps);}

inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}

int cross(int x,int y){return floor((a[x].b-a[y].b)/(a[y].k-a[x].k));}
int calc(int x,int y,int pos)
{
if(!x&&!y) return 0;
if(!x) return y;if(!y) return x;
double xx=a[x].f(pos),yy=a[y].f(pos);
int flag=sgn(xx-yy);
if(flag==0) return x<y?x:y;
return flag>0?x:y;
}

void update(int pos,int idx)
{
if(!poi[pos]) poi[pos]=idx;
else
{
double x=a[idx].f(pos),y=a[poi[pos]].f(pos);
int flag=sgn(x-y);
if(flag>0||(flag==0&&idx<poi[pos])) poi[pos]=idx;
}
}
void insert(int l,int r,int rt,int left,int right,int x)
{
if(l==left&&right==r)
{
if(!node[rt].idx) node[rt].idx=x;
else
{
bool L=sgn(a[x].f(l)-a[node[rt].idx].f(l))>0,R=sgn(a[x].f(r)-a[node[rt].idx].f(r))>0;
if(L&&R) node[rt].idx=x;
else if(L||R)
{
int mid=(l+r)>>1,tar=cross(x,node[rt].idx);
if(tar<=mid&&L) insert(lson,left,mid,x);
if(tar<=mid&&R) insert(lson,left,mid,node[rt].idx),node[rt].idx=x;
if(tar>mid&&L) insert(rson,mid+1,right,node[rt].idx),node[rt].idx=x;
if(tar>mid&&R) insert(rson,mid+1,right,x);
}
else update(l,x),update(r,x);
}
return ;
}
int mid=(l+r)>>1;
if(right<=mid) insert(lson,left,right,x);
else if(left>mid) insert(rson,left,right,x);
else insert(lson,left,mid,x),insert(rson,mid+1,right,x);
}
int query(int l,int r,int rt,int pos)
{
int res=node[rt].idx;
if(l==r) return res;
int mid=(l+r)>>1;
if(pos<=mid) return calc(res,query(lson,pos),pos);
else return calc(res,query(rson,pos),pos);
}

int main()
{
n=read();
int cnt=0;
while(n--)
{
int opt=read();
if(opt==0)
{
int x=(read()+last-1)%mr+1;
last=query(1,mr,1,x);
if(poi[x]) if((sgn(a[poi[x]].f(x)-a[last].f(x))==0&&poi[x]<last)) last=poi[x];
printf("%d\n",last);
}
else
{
int x0=(read()+last-1)%mr+1,y0=(read()+last-1)%mod+1,x1=(read()+last-1)%mr+1,y1=(read()+last-1)%mod+1;
if(x0>x1) swap(x0,x1),swap(y0,y1);
a[++tot]=line(x0,x1,y0,y1);
insert(1,mr,1,x0,x1,tot);
}
}
return 0;
}


Normal : BZOJ3938 机器人的每次移动(开始到停下)中时间与位置的关系都可以看成一个一次函数,于是变成维护一次函数最值问题

由于时间会很大,这里可以离散化也可以动态开点,都很好写但是动态开点可能要花费更多的空间

//BZOJ3938
#include<bits/stdc++.h>
#define debug(x) cout<<#x<<"="<<x<<endl
#define lson l,mid,node[rt].lc
#define rson mid+1,r,node[rt].rc
typedef long long ll;
using namespace std;
const int maxn = 500009;
const double eps = 1e-10;

struct line
{
double k,b;
line(ll _k=0,ll _b=0):k(1.0*_k),b(1.0*_b){}
double f(int x){return k*x+b;}
};
vector<pair<ll,ll> > a[maxn];
struct sg_tree
{
int lc,rc;
line up,down;
bool mx,mn;
}node[maxn*10];
int q[maxn],poi[maxn];
int n,m,mr,tot,root;
char opt[100];

inline int sgn(double x){return (x>-eps)-(x<eps);}
int cross(line x,line y){return floor((x.b-y.b)/(y.k-x.k));}

pair<ll,ll> merge(pair<ll,ll> x,pair<ll,ll> y){return make_pair(max(x.first,y.first),min(x.second,y.second));}

inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}

void insert_mx(int l,int r,int &rt,int left,int right,line x)
{
if(!rt) rt=++tot;
if(l==left&&right==r)
{
if(!node[rt].mx) node[rt].up=x,node[rt].mx=1;
else
{
bool L=sgn(x.f(l)-node[rt].up.f(l))>0,R=sgn(x.f(r)-node[rt].up.f(r))>0;
if(L&&R) node[rt].up=x;
else if(L||R)
{
int mid=(l+r)>>1,tar=cross(x,node[rt].up);
if(tar<=mid&&L) insert_mx(lson,left,mid,x);
if(tar<=mid&&R) insert_mx(lson,left,mid,node[rt].up),node[rt].up=x;
if(tar>mid&&L) insert_mx(rson,mid+1,right,node[rt].up),node[rt].up=x;
if(tar>mid&&R) insert_mx(rson,mid+1,right,x);
}
}
return ;
}int mid=(l+r)>>1;
if(right<=mid) insert_mx(lson,left,right,x);
else if(left>mid) insert_mx(rson,left,right,x);
else insert_mx(lson,left,mid,x),insert_mx(rson,mid+1,right,x);
}
void insert_mn(int l,int r,int &rt,int left,int right,line x)
{
if(!rt) rt=++tot;
if(l==left&&right==r)
{
if(!node[rt].mn) node[rt].down=x,node[rt].mn=1;
else
{
bool L=sgn(x.f(l)-node[rt].down.f(l))<0,R=sgn(x.f(r)-node[rt].down.f(r))<0;
if(L&&R) node[rt].down=x;
else if(L||R)
{
int mid=(l+r)>>1,tar=cross(x,node[rt].down);
if(tar<=mid&&L) insert_mn(lson,left,mid,x);
if(tar<=mid&&R) insert_mn(lson,left,mid,node[rt].down),node[rt].down=x;
if(tar>mid&&L) insert_mn(rson,mid+1,right,node[rt].down),node[rt].down=x;
if(tar>mid&&R) insert_mn(rson,mid+1,right,x);
}
}
return ;
}int mid=(l+r)>>1;
if(right<=mid) insert_mn(lson,left,right,x);
else if(left>mid) insert_mn(rson,left,right,x);
else insert_mn(lson,left,mid,x),insert_mn(rson,mid+1,right,x);
}
pair<ll,ll> query(int l,int r,int rt,int pos)
{
if(!rt) return make_pair(0,0);
pair<ll,ll> res=make_pair(node[rt].up.f(pos),node[rt].down.f(pos));
if(l==r) return res;
int mid=(l+r)>>1;
if(pos<=mid) return merge(query(lson,pos),res);
else return merge(query(rson,pos),res);
}

int main()
{
n=read();m=read();
for(int i=1;i<=n;i++) a[i].push_back(make_pair(0,read()));
for(int i=1;i<=m;i++)
{
int x=read();scanf("%s",opt+1);
if(opt[1]=='c')
{
int y=read(),z=read();
a[y].push_back(make_pair(x,z));
}
else q[++q[0]]=x;
mr=x;
}
for(int i=1;i<=n;i++) a[i].push_back(make_pair(mr,0));
for(int i=1;i<=n;i++)
{
int sz=a[i].size();
ll now=a[i][0].second;line x;x.k=0;x.b=now;
insert_mx(0,mr,root,a[i][0].first,a[i][1].first,x);
insert_mn(0,mr,root,a[i][0].first,a[i][1].first,x);
for(int j=1;j<sz-1;j++)
{
x.k=a[i][j].second;x.b=now-1ll*a[i][j].first*a[i][j].second;
insert_mx(0,mr,root,a[i][j].first,a[i][j+1].first,x);
insert_mn(0,mr,root,a[i][j].first,a[i][j+1].first,x);
now=now+1ll*(a[i][j+1].first-a[i][j].first)*a[i][j].second;
}
}
for(int i=1;i<=q[0];i++)
{
pair<ll,ll> res=query(0,mr,1,q[i]);
printf("%lld\n",max(res.first,-res.second));
}
return 0;
}
Hard : BZOJ4515 搬到了树上...那就链剖一发退化成链

一条链的操作可以变成先上去再下来,把式子拆开得到线段的斜率和截距

横坐标对应离散化后的树上前缀和,由于链剖序的特殊性,线段树下标对应横坐标并不单调,但又因为修改一定对应一条重链的一部分,这一定是单调的

离散化后的李超线段树基本没有变化,只需判断新线段与旧线段的交点在哪部分即可

区间查询的话就类似一般标记永久化线段树,记录每个点与儿子们的信息和,整块直接调用,非整块的话显然极值一定在左右端点,算一下取最小值即可

//BZOJ4515
#include<bits/stdc++.h>
#define debug(x) cout<<#x<<"="<<x<<endl
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
typedef long long ll;
using namespace std;
const int maxn = 100009;
const ll inf = 123456789123456789ll;
const double eps = 1e-10;

struct line
{
ll k,b;
ll f(ll x){return k*x+b;}
};
int first[maxn];
struct edg{int next,to,val;}e[maxn<<1];
struct sg_tree{line x;ll mn;bool cover;}node[maxn<<2];
int fa[maxn],dep[maxn],siz[maxn],son[maxn],top[maxn],pos[maxn],idx[maxn],len[maxn];
ll val[maxn];
int n,m,cnt,e_sum;

inline int sgn(double x){return (x>-eps)-(x<eps);}
ll cross(line x,line y){return (1.00*(x.b-y.b)/(y.k-x.k));}

inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
inline void add_edg(int x,int y,int z)
{
e_sum++;
e[e_sum].next=first[x];
first[x]=e_sum;
e[e_sum].to=y;
e[e_sum].val=z;
}

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 dfs1(int x,int f)
{
fa[x]=f;dep[x]=dep[f]+1;siz[x]=1;
for(int i=first[x];i;i=e[i].next)
{
int w=e[i].to;
if(w==f) continue;
dfs1(w,x);
siz[x]+=siz[w];len[w]=e[i].val;
if(siz[w]>siz[son[x]]) son[x]=w;
}
}
void dfs2(int x,int t)
{
pos[x]=++cnt;idx[cnt]=x;top[x]=t;
val[x]=val[fa[x]]+len[x];
if(son[x]) dfs2(son[x],t);
for(int i=first[x];i;i=e[i].next)
{
int w=e[i].to;
if(w==fa[x]||w==son[x]) continue;
dfs2(w,w);
}
}

inline void update(int l,int r,int rt)
{
node[rt].mn=min(node[rt<<1].mn,node[rt<<1|1].mn);
if(node[rt].cover) node[rt].mn=min(node[rt].mn,min(node[rt].x.f(val[idx[l]]),node[rt].x.f(val[idx[r]])));
}
void build(int l,int r,int rt)
{
if(l==r)
{
node[rt].mn=inf;
return ;
}int mid=(l+r)>>1;
build(lson);build(rson);
update(l,r,rt);
}
void insert(int l,int r,int rt,int left,int right,line x)
{
if(l==left&&right==r)
{
if(!node[rt].cover) node[rt].x=x,node[rt].cover=1;
else
{
bool L=sgn(x.f(val[idx[l]])-node[rt].x.f(val[idx[l]]))<0,R=sgn(x.f(val[idx[r]])-node[rt].x.f(val[idx[r]]))<0;
if(L&&R) node[rt].x=x;
else if(L||R)
{
int mid=(l+r)>>1;ll tar=cross(x,node[rt].x);
if(tar<=val[idx[mid]]&&L) insert(lson,left,mid,x);
if(tar<=val[idx[mid]]&&R) insert(lson,left,mid,node[rt].x),node[rt].x=x;
if(tar>val[idx[mid]]&&L) insert(rson,mid+1,right,node[rt].x),node[rt].x=x;
if(tar>val[idx[mid]]&&R) insert(rson,mid+1,right,x);
}
}
node[rt].mn=min(node[rt].mn,(ll)min(node[rt].x.f(val[idx[l]]),node[rt].x.f(val[idx[r]])));
return ;
}int mid=(l+r)>>1;
if(right<=mid) insert(lson,left,right,x);
else if(left>mid) insert(rson,left,right,x);
else insert(lson,left,mid,x),insert(rson,mid+1,right,x);
update(l,r,rt);
}
ll query(int l,int r,int rt,int left,int right)
{
ll res=inf;
if(node[rt].cover) res=min(node[rt].x.f(val[idx[left]]),node[rt].x.f(val[idx[right]]));
if(l==left&&right==r) return min(res,node[rt].mn);
int mid=(l+r)>>1;
if(right<=mid) return min(res,query(lson,left,right));
else if(left>mid) return min(res,query(rson,left,right));
else return min(res,min(query(lson,left,mid),query(rson,mid+1,right)));
}

void modify(int s,int t)
{
int anc=lca(s,t),a=read(),b=read(),now;line x;

now=s;x=(line){-1ll*a,1ll*(a*val[s]+b)};
while(top[now]!=top[anc])
{
insert(1,n,1,pos[top[now]],pos[now],x);
now=fa[top[now]];
}
insert(1,n,1,pos[anc],pos[now],x);

now=t;x=(line){1ll*a,(1ll*(val[s]-2*val[anc])*a+b)};
while(top[now]!=top[anc])
{
insert(1,n,1,pos[top[now]],pos[now],x);
now=fa[top[now]];
}
insert(1,n,1,pos[anc],pos[now],x);
}
ll ask(int x,int y)
{
ll res=inf;
while(top[x]!=top[y])
{
if(dep[top[x]]<dep[top[y]]) swap(x,y);
res=min(res,query(1,n,1,pos[top[x]],pos[x]));
x=fa[top[x]];
}
if(dep[x]<dep[y]) swap(x,y);
res=min(res,query(1,n,1,pos[y],pos[x]));
return res;
}

int main()
{
n=read();m=read();
for(int i=1;i<n;i++)
{
int x=read(),y=read(),z=read();
add_edg(x,y,z);add_edg(y,x,z);
}
dfs1(1,0);dfs2(1,1);build(1,n,1);
while(m--)
{
int opt=read(),s=read(),t=read();
if(opt==1) modify(s,t);
else printf("%lld\n",ask(s,t));
}
return 0;
}


Lunatic : 少女施工中...
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  数据结构