洛谷4097 HEOI2013 Segment 李超树

题目链接

题意:有 n n n次操作,每次操作可能有两种,第一种是插入一条线段,告诉你线段的两个端点,其中 1 < = x < = 39990 1<=x<=39990 1<=x<=39990 1 < = y < = 1 e 9 1<=y<=1e9 1<=y<=1e9,保证端点都是整数。第二种操作是询问与 x = k x=k x=k这条直线相交的所有线段的 k k k这个横坐标下的纵坐标的最大值。强制在线。 n < = 1 e 5 n<=1e5 n<=1e5。如果有多个线段,输出编号最小的。

题解:
李超树模板题。注意题目中可能存在线段是一个点的情况,也就是两个端点 x x x相同,这样要求取最大的那个 y y y。其他的没什么好说的。只要会李超树就知道该怎么做,不会的看我这篇博客

代码:

#include <bits/stdc++.h>
using namespace std;

int n,ans,cnt;
double eps=1e-8;
struct line
{
	double k,b;
}li[100010];
struct node
{
	int l,r,id;
}tr[400010];
inline void build(int rt,int l,int r)
{
	tr[rt].l=l;
	tr[rt].r=r;
	tr[rt].id=0;
	if(l==r)
	return;
	int mid=(l+r)>>1;
	build(rt<<1,l,mid);
	build(rt<<1|1,mid+1,r);
}
inline double calc(int id,int x)
{
	return li[id].k*x+li[id].b;
}
inline void update(int rt,int le,int ri,int id)
{
	int l=tr[rt].l,r=tr[rt].r;
	if(le<=l&&r<=ri)
	{
		int mid=(l+r)>>1;
		if(calc(id,l)>=calc(tr[rt].id,l)+eps&&calc(id,r)>=calc(tr[rt].id,r)+eps)
		tr[rt].id=id;
		else if(calc(id,l)>=calc(tr[rt].id,l)+eps)
		{
			if(calc(id,mid)>=calc(tr[rt].id,mid)+eps)
			{
				update(rt<<1|1,le,ri,tr[rt].id);
				tr[rt].id=id;
			}
			else
			update(rt<<1,le,ri,id);
		}
		else if(calc(id,r)>=calc(tr[rt].id,r)+eps)
		{
			if(calc(id,mid)>=calc(tr[rt].id,mid)+eps)
			{
				update(rt<<1,le,ri,tr[rt].id);
				tr[rt].id=id;
			}
			else
			update(rt<<1|1,le,ri,id);
		}
		return;
	}
	int mid=(l+r)>>1;
	if(le<=mid)
	update(rt<<1,le,ri,id);
	if(mid+1<=ri)
	update(rt<<1|1,le,ri,id);
}
inline int query(int rt,int x)
{
	int l=tr[rt].l,r=tr[rt].r;
	if(l==r)
	return tr[rt].id;
	int mid=(l+r)>>1;
	int res=0;
	if(x<=mid)
	res=query(rt<<1,x);
	else
	res=query(rt<<1|1,x);
	if(res==0)
	return tr[rt].id;
	else if(li[res].k*x+li[res].b>li[tr[rt].id].k*x+li[tr[rt].id].b+eps)
	return res;
	else if(fabs(calc(res,x)-calc(tr[rt].id,x))<=eps&&res<tr[rt].id)
	return res;
	else
	return tr[rt].id;
}
int main()
{
	build(1,1,50000);
	li[0].k=-2.0;
	li[0].b=-1000000000.0;
	scanf("%d",&n);
	for(int i=1;i<=n;++i)
	{
		int opt,a,b,c,d;
		scanf("%d",&opt);
		if(opt==0)
		{
			scanf("%d",&a);
			a=(a+ans-1)%39989+1;
			ans=query(1,a);
			printf("%d\n",ans);
		}
		else
		{
			scanf("%d%d%d%d",&a,&b,&c,&d);
			a=(a+ans-1)%39989+1;
			b=(b+ans-1)%1000000000+1;
			c=(c+ans-1)%39989+1;
			d=(d+ans-1)%1000000000+1;
			if(a==c)
			{
				li[++cnt].k=0;
				li[cnt].b=max(b,d);
			}
			else
			{
				li[++cnt].k=1.0*(d-b)/(1.0*(c-a));
				li[cnt].b=1.0*b-li[cnt].k*a;
			}
			update(1,min(a,c),max(a,c),cnt);
		}
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值