李超线段树
学习资料
传送门
里面对李超线段树的解析非常详尽,因此我就不再对其进行讲述
例题
T1 P4254
题解
板题
其实该题就是告诉我们多个一次函数(直线),让我们求出单点的最大值。
李超线段树就是用来解决如此的问题,它用到的是
l
a
z
y
−
t
a
g
lazy-tag
lazy−tag
直接见下方代码即可
代码
#include<bits/stdc++.h>
#define M 4000009
using namespace std;
char s[10];
int n,tag[M],tot;
struct l{double k,b;}line[M];
double getsum(int x,int id){return line[id].k*(x-1)+line[id].b;}//注意此处为(x-1)
void update(int k,int l,int r,int id){
if(l==r){
if(getsum(l,id)>getsum(l,tag[k])) tag[k]=id;
return;
}int mid=(l+r)>>1;double ans1=getsum(mid,id),ans2=getsum(mid,tag[k]);
if(line[id].k>line[tag[k]].k){
if(ans1>ans2) update(k<<1,l,mid,tag[k]),tag[k]=id;
else update(k<<1|1,mid+1,r,id);
}else{
if(ans1>ans2) update(k<<1|1,mid+1,r,tag[k]),tag[k]=id;
else update(k<<1,l,mid,id);
}
}
double query(int k,int l,int r,int pos){
if(l==r) return getsum(pos,tag[k]);
int mid=(l+r)>>1;double ans=getsum(pos,tag[k]);
if(pos<=mid) ans=max(ans,query(k<<1,l,mid,pos));
else ans=max(ans,query(k<<1|1,mid+1,r,pos));
return ans;
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%s",s);int x;
if(s[0]=='P') tot++,scanf("%lf%lf",&line[tot].b,&line[tot].k),update(1,1,50001,tot);
if(s[0]=='Q') scanf("%d",&x),printf("%d\n",(int)query(1,1,50001,x)/100);
}return 0;
}
T2 P4097
题解
板题,注意取模即可
代码
#include<bits/stdc++.h>
#define int long long
#define M 1000009
using namespace std;
const int mod=1e9;
int n,tag[M],tot,lastans=-1;
struct l{double k,b;}line[M];
int read(){
int f=1,re=0;char ch;
for(ch=getchar();!isdigit(ch)&&ch!='-';ch=getchar());
if(ch=='-'){f=-1,ch=getchar();}
for(;isdigit(ch);ch=getchar()) re=(re<<3)+(re<<1)+ch-'0';
return re*f;
}
double getsum(int x,int id){return line[id].k*x+line[id].b;}
void update(int k,int l,int r,int ql,int qr,int id){
if(l>=ql&&r<=qr){
if(l==r){
if(getsum(l,id)>getsum(l,tag[k])) tag[k]=id;
return;
}int mid=(l+r)>>1;double ans1=getsum(mid,id),ans2=getsum(mid,tag[k]);
if(line[id].k>line[tag[k]].k){
if(ans1>ans2) update(k<<1,l,mid,ql,qr,tag[k]),tag[k]=id;
else update(k<<1|1,mid+1,r,ql,qr,id);
}else{
if(ans1>ans2) update(k<<1|1,mid+1,r,ql,qr,tag[k]),tag[k]=id;
else update(k<<1,l,mid,ql,qr,id);
}return;
}int mid=(l+r)>>1;
if(ql<=mid) update(k<<1,l,mid,ql,qr,id);
if(qr>mid) update(k<<1|1,mid+1,r,ql,qr,id);
}
double query(int k,int l,int r,int pos){
if(l==r) return tag[k];
int mid=(l+r)>>1;
if(pos<=mid){
int w=query(k<<1,l,mid,pos);
if(getsum(pos,tag[k])>getsum(pos,w)) return tag[k];
else return w;
}else{
int w=query(k<<1|1,mid+1,r,pos);
if(getsum(pos,tag[k])>getsum(pos,w)) return tag[k];
else return w;
}
}
signed main(){
n=read();
for(int i=1;i<=n;i++){
int opt=read(),v;
if(!opt) v=read(),printf("%lld\n",lastans=query(1,1,40000,(v+lastans+39989)%39989+1)),lastans--;
else{
int x=(read()+lastans+39989)%39989+1;
int y=(read()+lastans+mod)%mod+1;
int z=(read()+lastans+39989)%39989+1;
int w=(read()+lastans+mod)%mod+1;
//printf("%lld %lld %lld %lld\n",x,y,z,w);
if(x>z) swap(x,z),swap(y,w);
if(x==z) line[++tot].k=0,line[tot].b=max(y,w);
else line[++tot].k=(double)(y-w)/(x-z),line[tot].b=(double)(y-line[tot].k*x);
update(1,1,40000,x,z,tot);
}
}return 0;
}
T3 P4069
题解
树剖套李超线段树
首先我们发现对于路径
(
u
,
v
)
(u,v)
(u,v)上赋值,我们可以将它划分为两段(然后分别满足关于
d
i
s
[
i
]
dis[i]
dis[i]的一次函数)
第一段
(
u
,
l
c
a
)
(u,lca)
(u,lca)
y
=
−
a
×
d
i
s
[
i
]
+
(
a
×
d
i
s
[
s
]
+
b
)
y=−a×dis[i]+(a×dis[s]+b)
y=−a×dis[i]+(a×dis[s]+b)
第二段
(
l
c
a
,
v
)
(lca,v)
(lca,v)
y
=
a
×
d
i
s
[
i
]
+
a
×
(
d
i
s
[
s
]
−
2
×
d
i
s
[
x
]
)
y=a×dis[i]+a×(dis[s]−2×dis[x])
y=a×dis[i]+a×(dis[s]−2×dis[x])
然后就是板子了,树剖套李超线段树
代码
#include<bits/stdc++.h>
#define int long long
#define M 100009
using namespace std;
int read(){
int f=1,re=0;char ch;
for(ch=getchar();!isdigit(ch)&&ch!='-';ch=getchar());
if(ch=='-'){f=-1,ch=getchar();}
for(;isdigit(ch);ch=getchar()) re=(re<<3)+(re<<1)+ch-'0';
return re*f;
}
const int inf=123456789123456789ll;
int n,m,dep[M],son[M],siz[M],idx[M],dis[M],w[M*2],first[M],to[M*2],nxt[M*2],cnt,cal,tot,num[M],f[M],top[M];
struct tree{int l,r,tag,minx;}tr[M*4];
struct line{int k,b;}li[M*4];
//李超线段树
int getsum(int x,int id){return li[id].k*dis[idx[x]]+li[id].b;}
void pushup(int k){tr[k].minx=min(tr[k].minx,min(tr[k<<1].minx,tr[k<<1|1].minx));}
void build(int k,int l,int r){
tr[k].l=l,tr[k].r=r,tr[k].tag=1,tr[k].minx=inf;
if(l==r) return;
int mid=(l+r)>>1;
build(k<<1,l,mid),build(k<<1|1,mid+1,r);
}
void change(int k,int ql,int qr,int id){
if(tr[k].l>=ql&&tr[k].r<=qr){
if(tr[k].l==tr[k].r){
if(getsum(tr[k].l,id)<=getsum(tr[k].l,tr[k].tag)) tr[k].tag=id,tr[k].minx=getsum(tr[k].l,id);
return;
}int mid=(tr[k].r+tr[k].l)>>1;int ans1=getsum(mid,id),ans2=getsum(mid,tr[k].tag);
if(li[id].k<li[tr[k].tag].k){
if(ans1<=ans2) change(k<<1,ql,qr,tr[k].tag),tr[k].tag=id;
else change(k<<1|1,ql,qr,id);
}else{
if(ans1<=ans2) change(k<<1|1,ql,qr,tr[k].tag),tr[k].tag=id;
else change(k<<1,ql,qr,id);
}tr[k].minx=min(tr[k].minx,min(getsum(tr[k].l,id),getsum(tr[k].r,id)));
pushup(k);return;
}int mid=(tr[k].l+tr[k].r)>>1;
if(ql<=mid) change(k<<1,ql,qr,id);
if(qr>mid) change(k<<1|1,ql,qr,id);
pushup(k);
}
int query(int k,int ql,int qr){
if(tr[k].l>=ql&&tr[k].r<=qr) return tr[k].minx;
int mid=(tr[k].l+tr[k].r)>>1,ans=inf;
if(li[tr[k].tag].b!=inf) ans=min(getsum(max(tr[k].l,ql),tr[k].tag),getsum(min(tr[k].r,qr),tr[k].tag));
if(ql<=mid) ans=min(ans,query(k<<1,ql,qr));
if(qr>mid) ans=min(ans,query(k<<1|1,ql,qr));
return ans;
}
//树剖
void add(int x,int y,int z){nxt[++tot]=first[x],first[x]=tot,to[tot]=y,w[tot]=z;}
void dfs1(int u,int fa){
dep[u]=dep[fa]+1,siz[u]=1;
for(int i=first[u];i;i=nxt[i]){
int v=to[i];
if(v==fa) continue;
dis[v]=dis[u]+w[i];
f[v]=u,dfs1(v,u);
if(siz[v]>siz[son[u]]) son[u]=v;
siz[u]+=siz[v];
}
}
void dfs2(int u,int tp){
top[u]=tp,num[u]=++cal,idx[cal]=u;
if(son[u]) dfs2(son[u],tp);
for(int i=first[u];i;i=nxt[i]){
int v=to[i];
if(!num[v]) dfs2(v,v);
}
}
int getlca(int x,int y){
while(top[x]!=top[y]){
if(dep[top[x]]<dep[top[y]]) swap(x,y);
x=f[top[x]];
}if(dep[x]>dep[y])swap(x,y);
return x;
}
int solve(int x,int y){
int ans=inf;
while(top[x]!=top[y]){
if(dep[top[x]]<dep[top[y]]) swap(x,y);
ans=min(ans,query(1,num[top[x]],num[x]));
x=f[top[x]];
}if(dep[x]>dep[y])swap(x,y);
ans=min(ans,query(1,num[x],num[y]));
return ans;
}
void update(int x,int y,int id){
while(top[x]!=top[y]){
if(dep[top[x]]<dep[top[y]]) swap(x,y);
change(1,num[top[x]],num[x],id);
x=f[top[x]];
}if(dep[x]>dep[y])swap(x,y);
change(1,num[x],num[y],id);
}
signed main(){
n=read(),m=read();
for(int i=1;i<n;i++){
int x=read(),y=read(),z=read();
add(x,y,z),add(y,x,z);
}dfs1(1,0),dfs2(1,1),build(1,1,n);
li[++cnt].k=0,li[cnt].b=inf;
for(int i=1;i<=m;i++){
int opt=read(),x=read(),y=read();
if(opt==1){
int a=read(),b=read(),lca=getlca(x,y);
li[++cnt].k=-a,li[cnt].b=a*dis[x]+b,update(x,lca,cnt);
li[++cnt].k=a,li[cnt].b=a*(dis[x]-2*dis[lca])+b,update(lca,y,cnt);
}else printf("%lld\n",solve(x,y));
}return 0;
}