李超线段树

现在要求你在线动态维护一个二维平面直角坐标系,支持插入一条线段,询问与直线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 : 少女施工中...

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值