NOIp模拟 光线追踪

传送门
这种问题考虑怎么一步一步把问题化简。
首先可以把一个矩形右边和上面的边都丢掉。和那个 s p e i k e speike speike有点像。
然后可以把剩下的横边和竖边独立开来看。如果可以分别求出询问向量所碰到的横和竖的最短距离问题就解决了。取最小距离即可。
对于插入的横/竖边,在对应的角度区间里面更新一下最小的横/纵坐标以及对应的编号即可。
查询碰到的最近的竖/横边就是单点查询最小值。

于是把角度离散化一下就可以用线段树维护了。
坐标相同的时候取编号大的,这个用 p a i r pair pair很好做。
区间修改采用标记永久化的方法做会很方便,不能 p u s h u p pushup pushup

询问的时候要特判一下 x = 0   o r   y = 0 x=0\ or\ y=0 x=0 or y=0

#include<bits/stdc++.h>
#define ll long long
#define re register
#define cs const
#define pii std::pair<int,int>
#define mp std::make_pair
#define fi first
#define se second

cs int N=1e5+10,oo=1e9+1;
namespace IO{
	cs int Rlen=1<<22|1;
	char buf[Rlen],*p1,*p2;
	inline char gc(){return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;}
	template<typename T>
	inline T get(){
		char ch=gc();T x=0;
		while(!isdigit(ch)) ch=gc();
		while(isdigit(ch)) x=(x+(x<<2)<<1)+(ch^48),ch=gc();
		return x;
	}
	inline int gi(){return get<int>();}
}
using IO::gi;

struct Point{
	int x,y;
	inline double k(){return x?1.0*y/x:1e18;}
};
struct OP{int op,dk[3];Point P[3];double k[3];}a[N];

int Q,M;double st[N*3];int top=0;
inline void disc(){
	std::sort(st+1,st+top+1);
	M=std::unique(st+1,st+top+1)-st-1;
	for(int re i=1;i<=Q;++i) for(int re j=0;j<3;++j)
		a[i].dk[j]=std::lower_bound(st+1,st+M+1,a[i].k[j])-st;
}
#define mid (T[root].l+T[root].r>>1)
#define lc (root<<1)
#define rc (root<<1|1)
struct node{int l,r;pii h;};
inline pii pmin(cs pii &p,cs pii &q){
	return (p.fi==q.fi)?(p.se>q.se?p:q):(p.fi<q.fi?p:q);
}
inline void pMin(pii &p,cs pii &q){
	p=(p.fi==q.fi)?(p.se>q.se?p:q):(p.fi<q.fi?p:q);;
}
struct SGT{
	node T[(N*3)<<2];
	inline void build(int root,int l,int r){
		T[root].l=l,T[root].r=r,T[root].h=mp(oo,0);
		if(l==r) return;
		build(lc,l,mid),build(rc,mid+1,r);
	}
	inline void insert(int root,int l,int r,cs pii &now){
		if(l<=T[root].l&&T[root].r<=r) return pMin(T[root].h,now);
		if(l> mid) return insert(rc,l,r,now);
		if(r<=mid) return insert(lc,l,r,now);
		return insert(lc,l,mid,now),insert(rc,mid+1,r,now);
	}
	inline pii query(int root,int pos){
		if(T[root].l==T[root].r) return T[root].h;
		return pmin(T[root].h,(pos<=mid)?query(lc,pos):query(rc,pos));
	}
}TREE[2];
#undef mid
#undef lc
#undef rc
int main(){
//	freopen("raytracing.in","r",stdin);
	Q=gi();
	for(int re i=1;i<=Q;++i){
		a[i].op=gi();
		if(a[i].op==1)
			a[i].P[0].x=a[i].P[1].x=gi(),
			a[i].P[0].y=a[i].P[2].y=gi(),
			a[i].P[2].x=gi(),a[i].P[1].y=gi(),
			st[++top]=a[i].k[0]=a[i].P[0].k(),st[++top]=a[i].k[1]=a[i].P[1].k(),st[++top]=a[i].k[2]=a[i].P[2].k();
			
		if(a[i].op==2) a[i].P[0].x=gi(),a[i].P[0].y=gi(),st[++top]=a[i].k[0]=a[i].P[0].k();
	}disc(),TREE[0].build(1,1,M),TREE[1].build(1,1,M);

	for(int re i=1;i<=Q;++i){
		if(a[i].op==1){
			TREE[0].insert(1,a[i].dk[0],a[i].dk[1],mp(a[i].P[0].x,i));
			TREE[1].insert(1,a[i].dk[2],a[i].dk[0],mp(a[i].P[0].y,i));
		}
		if(a[i].op==2){
			pii q0=TREE[0].query(1,a[i].dk[0]),q1=TREE[1].query(1,a[i].dk[0]);
			if(!(q0.se+q1.se)) puts("0");
			else if(!q0.se||!q1.se) printf("%d\n",q0.se+q1.se);
			else if(a[i].P[0].y==0) printf("%d\n",q0.fi<a[q1.se].P[0].x?q0.se:q1.se);
			else if(a[i].P[0].x==0) printf("%d\n",q1.fi<a[q0.se].P[0].y?q1.se:q0.se);
			else{
				ll mul1=1ll*q1.fi*a[i].P[0].x,mul0=1ll*q0.fi*a[i].P[0].y;
				printf("%d\n",(mul1==mul0)?(q0.se>q1.se?q0.se:q1.se):(mul1>mul0?q0.se:q1.se));
			}
		}
	}
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值