CCFCSP认证202206-04光线追踪解析

前言:9月17号就要参加csp认证了,最近一直在练习第四题的部分分,感觉这道题在第四题中算简单的,有了思路比较容易得满分。

这题坐标数据范围并没有指明,但是|x1-x2|的绝对值之和不超过300005;看到这个数据,我们就可以发现如果把镜子给抽象成n-2个可反射光线的点,使用nlogn的算法可以在2.0s内完成计算。(如果只想拿15分或35分的话应该可以使用n方的算法)

由于镜子与坐标轴成45°夹角,所以我们可以将镜子分为两种类型

由于光线可以向四个方向移动,沿着x轴平行方向时y值保持不变,沿着y轴平行方向时x值保持不变,所以我们可以用set来维护两种不同移动方向下的镜子坐标。(第一遍写的时候把结构体名字hang、lie写反了,没有影响,后面取值的时候h、l的对应关系是正确的)我们需要定义一下‘<’操作符来让set对结构体进行排序

struct hang{
	int x,y;
	double a;
	bool type;
	bool operator<(const hang w)const{
		if(x==w.x) return y<w.y;
		return x<w.x;
	}
};
struct lie{
	int x,y;
	double a;
	bool type;
	bool operator<(const lie w)const{
		if(y==w.y) return x<w.x;
		return y<w.y;
	}
};
set<hang> l;
set<lie> h;

操作1:

读入时需要用数组来记录一下不同操作序号所插入的反射面的坐标来方便操作2的删除,如果直接for遍历一遍set来删除镜面的话应该会tl。

if(op==1){
			int x1,y1,x2,y2;
			double a;
			scanf("%d%d%d%d%lf",&x1,&y1,&x2,&y2,&a);
			int minx=min(x1,x2),maxx=max(x1,x2),miny=min(y1,y2),maxy=max(y1,y2);
			if((x1<x2&&y1<y2)||(x1>x2&&y1>y2)){	
				for(int j=minx+1,k=1;j<maxx;j++,k++){
					h.insert({j,miny+k,a,0});
					l.insert({j,miny+k,a,0});
					n++;
					pos[n][0]=j;
					pos[n][1]=miny+k;
					ope[n]=i;
				}
			}
			else{
				for(int j=minx+1,k=1;j<maxx;j++,k++){
					h.insert({j,maxy-k,a,1});
					l.insert({j,maxy-k,a,1});
					n++;
					pos[n][0]=j;
					pos[n][1]=maxy-k;
					ope[n]=i;
				}
			}
		}

操作2:

由于操作1时已经对数据进行过预处理,所以删除操作很简便

else if(op==2){
			int k;
			scanf("%d",&k);
			int p=lower_bound(ope+1,ope+n+1,k)-ope;
			for(int i=p;ope[i]==k;i++){
				h.erase({pos[i][0],pos[i][1]});
				l.erase({pos[i][0],pos[i][1]});
			}
		}

操作3:

本人的操作3写的异常丑陋,各位看官仅做个参考,注意考虑set的边界问题,需要检查寻找坐标时迭代器值会不会指向h.end()||h.begin(),这写操作只要细心一点不会出问题。还有一点需要注意!!!操作3有两种结束条件:分别是t<=0和I<0,我在写代码时只考虑了t<=这一种结束条件,导致100分的代码变成了30分tl。最后找大佬检查了一个小时,把while(1)改成while(I>=1)就过了,哭死。

在这里给大家提供一个可简化操作3代码的方法,直接特判一下起始点在不在镜子上,再对d=0,1,2,3进行分类讨论,可读性大幅提高的同时代码量也更小

else{
			int x,y,d,t;
			double I;
			scanf("%d%d%d%lf%d",&x,&y,&d,&I,&t);
			t++;
			if(d==0) x--;
			if(d==2) x++;
			if(d==1) y--;
			if(d==3) y++;
			while(I>=1){				
				if(d==0){
					ith=h.upper_bound({x,y});
					if(ith==h.end()||ith->y!=y||ith->x-x>t){
						x+=t;
						break;
					}
					I*=ith->a;
					t-=ith->x-x;
					x=ith->x;
					if(ith->type==0) d=1;
					else d=3;
				}
				else if(d==2){
					ith=h.lower_bound({x,y});
					if(ith==h.begin()){
						x-=t;
						break;
					}
					ith--;
					if(ith->y!=y||x-ith->x>t){
						x-=t;
						break;
					}
					I*=ith->a;
					t-=x-ith->x;
					x=ith->x;
					if(ith->type==0) d=3;
					else d=1;
				}
				else if(d==1){					
					itl=l.upper_bound({x,y});
					if(itl==l.end()||itl->x!=x||itl->y-y>t){
						y+=t;
						break;
					}
					I*=itl->a;
					t-=itl->y-y;
					y=itl->y;
					if(itl->type==0) d=0;
					else d=2;
				}
				else{
					itl=l.lower_bound({x,y});
					if(itl==l.begin()){
						y-=t;
						break;
					}
					itl--;
					if(itl->x!=x||y-ith->y>t){
						y-=t;
						break;
					}
					I*=itl->a;
					t-=y-itl->y;
					y=itl->y;
					if(itl->type==0) d=2;
					else d=0;
				}
			}
			if(I<1) printf("0 0 0\n");
			else printf("%d %d %d\n",x,y,(int)I);
		}

总代码:

#include<bits/stdc++.h>
using namespace std;
int pos[300005][2]={0},ope[300005];
struct hang{
	int x,y;
	double a;
	bool type;
	bool operator<(const hang w)const{
		if(x==w.x) return y<w.y;
		return x<w.x;
	}
};
struct lie{
	int x,y;
	double a;
	bool type;
	bool operator<(const lie w)const{
		if(y==w.y) return x<w.x;
		return y<w.y;
	}
};
set<hang> l;
set<lie> h;
int jue(int x){
	if(x<0) return -x;
	else return x;
}
int main(){
	int m,n=0;
	scanf("%d",&m);
	set<hang>::iterator itl;
	set<lie>::iterator ith;
	for(int i=1;i<=m;i++){
		int op;
		scanf("%d",&op);
		if(op==1){
			int x1,y1,x2,y2;
			double a;
			scanf("%d%d%d%d%lf",&x1,&y1,&x2,&y2,&a);
			int minx=min(x1,x2),maxx=max(x1,x2),miny=min(y1,y2),maxy=max(y1,y2);
			if((x1<x2&&y1<y2)||(x1>x2&&y1>y2)){	
				for(int j=minx+1,k=1;j<maxx;j++,k++){
					h.insert({j,miny+k,a,0});
					l.insert({j,miny+k,a,0});
					n++;
					pos[n][0]=j;
					pos[n][1]=miny+k;
					ope[n]=i;
				}
			}
			else{
				for(int j=minx+1,k=1;j<maxx;j++,k++){
					h.insert({j,maxy-k,a,1});
					l.insert({j,maxy-k,a,1});
					n++;
					pos[n][0]=j;
					pos[n][1]=maxy-k;
					ope[n]=i;
				}
			}
		}
		else if(op==2){
			int k;
			scanf("%d",&k);
			int p=lower_bound(ope+1,ope+n+1,k)-ope;
			for(int i=p;ope[i]==k;i++){
				h.erase({pos[i][0],pos[i][1]});
				l.erase({pos[i][0],pos[i][1]});
			}
		}
		else{
			int x,y,d,t;
			double I;
			scanf("%d%d%d%lf%d",&x,&y,&d,&I,&t);
			t++;
			if(d==0) x--;
			if(d==2) x++;
			if(d==1) y--;
			if(d==3) y++;
			while(I>=1){				
				if(d==0){
					ith=h.upper_bound({x,y});
					if(ith==h.end()||ith->y!=y||ith->x-x>t){
						x+=t;
						break;
					}
					I*=ith->a;
					t-=ith->x-x;
					x=ith->x;
					if(ith->type==0) d=1;
					else d=3;
				}
				else if(d==2){
					ith=h.lower_bound({x,y});
					if(ith==h.begin()){
						x-=t;
						break;
					}
					ith--;
					if(ith->y!=y||x-ith->x>t){
						x-=t;
						break;
					}
					I*=ith->a;
					t-=x-ith->x;
					x=ith->x;
					if(ith->type==0) d=3;
					else d=1;
				}
				else if(d==1){					
					itl=l.upper_bound({x,y});
					if(itl==l.end()||itl->x!=x||itl->y-y>t){
						y+=t;
						break;
					}
					I*=itl->a;
					t-=itl->y-y;
					y=itl->y;
					if(itl->type==0) d=0;
					else d=2;
				}
				else{
					itl=l.lower_bound({x,y});
					if(itl==l.begin()){
						y-=t;
						break;
					}
					itl--;
					if(itl->x!=x||y-ith->y>t){
						y-=t;
						break;
					}
					I*=itl->a;
					t-=y-itl->y;
					y=itl->y;
					if(itl->type==0) d=2;
					else d=0;
				}
			}
			if(I<1) printf("0 0 0\n");
			else printf("%d %d %d\n",x,y,(int)I);
		}
	}
	return 0;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值