李超线段树
2017-07-28 21:44
134 查看
现在要求你在线动态维护一个二维平面直角坐标系,支持插入一条线段,询问与直线x=x0相交的所有线段中交点y的最大/最小值
--李超线段树解决的问题
现在我们只考虑询问最大,事实上最小是同理的
线段树维护覆盖这个区间的"最优势线段","最优势线段"指覆盖此区间且暴露最多的线段
比如
对于区间A红色为"最优势线段"
可以证明对于一个位置的询问,答案一定在所有包含这个位置的区间的"最优势线段"里
维护的话,考虑当前加入线段与原"最优势线段"的关系
还没有"最优势线段" -> 直接改
新的把旧的盖住了 -> 直接改
旧的把新的盖住了 -> 直接返回
有交点 -> 比较谁更优势,把不优势的下放到交点所在儿子区间上
询问的话自底向上把每个经过的区间的答案取最大值即可
一条线段会没分配到O(logn)个区间上,每个区间至多下放O(logn)次
修改操作复杂度O(log^2n),查询O(logn)
代码很好写...
例题:
Easy : BZOJ3165 BZOJ1568 都是裸题
Normal : BZOJ3938 机器人的每次移动(开始到停下)中时间与位置的关系都可以看成一个一次函数,于是变成维护一次函数最值问题
由于时间会很大,这里可以离散化也可以动态开点,都很好写但是动态开点可能要花费更多的空间
一条链的操作可以变成先上去再下来,把式子拆开得到线段的斜率和截距
横坐标对应离散化后的树上前缀和,由于链剖序的特殊性,线段树下标对应横坐标并不单调,但又因为修改一定对应一条重链的一部分,这一定是单调的
离散化后的李超线段树基本没有变化,只需判断新线段与旧线段的交点在哪部分即可
区间查询的话就类似一般标记永久化线段树,记录每个点与儿子们的信息和,整块直接调用,非整块的话显然极值一定在左右端点,算一下取最小值即可
Lunatic : 少女施工中...
--李超线段树解决的问题
现在我们只考虑询问最大,事实上最小是同理的
线段树维护覆盖这个区间的"最优势线段","最优势线段"指覆盖此区间且暴露最多的线段
比如
对于区间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 : 少女施工中...
相关文章推荐
- 【树链剖分+李超线段树】BZOJ4515(Sdoi2016)[游戏]题解
- 【BZOJ-1568】Blue Mary开公司 李超线段树 (标记永久化)
- BZOJ 1568 李超线段树
- [bzoj4515][Sdoi2016]游戏-树链剖分+李超线段树
- 【李超线段树】BZOJ1568[JSOI2008]Blue Mary开公司
- BZOJ 1568 李超线段树
- [李超线段树] BZOJ 3165: [Heoi2013]Segment
- BZOJ 3165 李超线段树
- [bzoj1568][JSOI2008]Blue Mary开公司——李超线段树
- BZOJ 3165 李超线段树
- [树链剖分+李超线段树] BZOJ4515: [Sdoi2016]游戏
- [bzoj3165][Heoi2013]Segment——李超线段树
- [bzoj1568]李超线段树模板题(标志永久化)
- 李超线段树
- [李超线段树] BZOJ3165 [Heoi2013]. Segment
- 【BZOJ-3165】Segment 李超线段树(标记永久化)
- 【BZOJ-4515】游戏 李超线段树 + 树链剖分 + 半平面交
- [李超线段树 模板题] BZOJ 1568 [JSOI2008]Blue Mary开公司
- 【李超线段树】BZOJ3165 [Heoi2013]Segment
- 【BZOJ4515】游戏,树链剖分+永久化标记线段树维护线段信息(李超线段树)