李超线段树

李超线段树

学习资料

传送门
里面对李超线段树的解析非常详尽,因此我就不再对其进行讲述

例题

T1 P4254

题解

板题
其实该题就是告诉我们多个一次函数(直线),让我们求出单点的最大值。
李超线段树就是用来解决如此的问题,它用到的是 l a z y − t a g lazy-tag lazytag
直接见下方代码即可

代码
#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) (uv)上赋值,我们可以将它划分为两段(然后分别满足关于 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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值