李超线段树 学习笔记

李超线段树 学习笔记

问题引入:

在一个平面内,有 \(n\) 次操作,这些操作分为 \(2\) 种:第一种是在平面内加入一条线段;第二种是给定一个 \(k\) ,查询直线 \(x=k\) 与这些线段交点的最大值。(强制在线, \(n\le10^5,1\le x\le 39989,-10^9\le y\le 10^9\)

求这种用区间覆盖的问题,一般我们会想到线段树。但是一般的线段树无法实现上述操作,于是李超线段树营运而生。

算法:

利用标记永久化技巧,李超线段树上的每一个区间内贮存该区间内的点询问时可能的答案线段,这样询问的复杂度为 \(O(\log n)\)

接下来是修改。我们将修改操作分为两个部分,第一部分是找到加入的线段覆盖的区间在线段树上的位置,第二部分是修改掉原来线段树上的值。

第一部分很好处理,然后是第二部分:

首先,若该位置没有贮存线段,则直接加入。

若该位置贮存了线段(设为 \(l_1\) ,加入的那条线段设为 \(l_2\) ),则考虑我们要下放左边还是右边的线段继续修改。此时我们会发现,如果 \(l_2\) 有一半的位置大于 \(l_1\) ,此时下放 \(l_1\) 更优。所以我们先判断在 \(x=mid\) 时,哪一条线段的值最大,就将哪一条留在当前区间,下放另一条。判断下放左边还是右边是,我们找出要下放的那条线段在哪里大于留下的线段,通过比较两条线段在 \(l\)\(r\) 处的取值实现。由于一段区间在线段树上最多被划分成 \(\log n\) 个小区间,而每个小区间最多下放 \(\log n\) 次,所以修改的时间复杂度为 \(O(log^2 n)\)

Code

#include<bits/stdc++.h>
using namespace std;
const int N=50005,M=1e9,MA=39989;double E=1e-14;
double tr[N*4][2],ma;int bh[N*4],ans[N],K,ss;bool bj[N*4];
void xg2(int K,double k,double b,int l,int r,int tot)
{
	double mid=1.0*(l+r)/2,k1=tr[tot][0],b1=tr[tot][1];
	if(bj[tot]==0)
	{
		bj[tot]=1;tr[tot][0]=k;tr[tot][1]=b;bh[tot]=K;return;
	}
	if(l==r)
	{
		if(abs(k*l+b-k1*l-b1)<=E)
		{
			if(bh[tot]>K)
			{
				tr[tot][0]=k;tr[tot][1]=b;bh[tot]=K;
			}
		}
		else
		{
			if(k*l+b>k1*l+b1)
			{
				tr[tot][0]=k;tr[tot][1]=b;bh[tot]=K;
			}
		}
		return;
	}
	if(k*mid+b>k1*mid+b1)
	{
		tr[tot][0]=k;tr[tot][1]=b;swap(K,bh[tot]);swap(k1,k);swap(b1,b);
	}
	if(k*l+b>k1*l+b1)
	xg2(K,k,b,l,mid,tot*2);
	else
	xg2(K,k,b,mid+1,r,tot*2+1);
}
void xg1(int K,double k,double b,int x,int y,int l,int r,int tot)
{
	if(x<=l&&r<=y)
	{
		xg2(K,k,b,l,r,tot);return;
	}
	int mid=(l+r)>>1;
	if(x<=mid)
	xg1(K,k,b,x,y,l,mid,tot*2);
	if(mid<y)
	xg1(K,k,b,x,y,mid+1,r,tot*2+1);
}
void cz(double x,int l,int r,int tot)
{
	if(bj[tot]==1)
	{
		if(abs(ma-tr[tot][0]*x-tr[tot][1])<=E)
		{
			if(K>bh[tot])
			K=bh[tot];
		}
		else
		{
			if(ma<tr[tot][0]*x+tr[tot][1])
			{
				ma=tr[tot][0]*x+tr[tot][1];K=bh[tot];
			}
		}
	}
	if(l==r)
	return;
	int mid=(l+r)/2;
	if(x<=mid)
	cz(x,l,mid,tot*2);
	else
	cz(x,mid+1,r,tot*2+1);
}
int main()
{
	int n,i,op,x,y,k=0;double x1,y1,x2,y2,t;
	scanf("%d",&n);
	for(i=1;i<=n;i++)
	{
		scanf("%d",&op);
		if(op==0)
		{
			scanf("%d",&x);
			x=(x+ans[ss]-1)%39989+1;
			ma=-1e18;K=0;cz(x,1,MA,1);
			ans[++ss]=K;
		}
		else
		{
			k++;
			scanf("%d%d",&x,&y);x1=(x+ans[ss]-1)%39989+1;y1=(y+ans[ss]-1)%M+1;
			scanf("%d%d",&x,&y);x2=(x+ans[ss]-1)%39989+1;y2=(y+ans[ss]-1)%M+1;
			if(x1>x2)
			{
				t=x1;x1=x2;x2=t;t=y1;y1=y2;y2=t;
			}
			if(x1==x2)
			{
				if(y1>y2)
				xg1(k,0,y1,x1,x2,1,MA,1);
				else
				xg1(k,0,y2,x1,x2,1,MA,1);
			}
			else
			xg1(k,(y1-y2)/(x1-x2),y1-(y1-y2)/(x1-x2)*x1,x1,x2,1,MA,1);
		}
	}
	for(i=1;i<=ss;i++)
	printf("%d\n",ans[i]);
	return 0;
}
posted @ 2024-07-05 21:27  Cyan_wind  阅读(8)  评论(0编辑  收藏  举报