CCF202206-4 光线追踪

CCF202206-4 光线追踪

试题编号:202206-4
试题名称:光线追踪
时间限制:2.0s
内存限制:512.0MB

问题描述

光线追踪是计算机图形学领域的一个重要算法,其原理是追踪一束从光源发出的光,经过不同的反射面,最终到达摄像机处的过程。

在这道问题中,你需要实现一段程序来处理一个简易的光线追踪模型。

在平面中有一些反射面,为方便起见,我们设这些反射面都是线段,与坐标轴成 45 度角摆放,且两个端点的坐标均为整数。为进一步简化问题,我们假设所有的反射表面都是镜面反射。任何一束光线照射到反射面上(为避免讨论,假设反射面不含端点)时,都会改变方向为相应的镜面反射方向。注意,反射面的两侧都可以反射光线。

平面中还有一些激光光源,每个光源位于一个坐标为整数的点上,会向某个水平或竖直的方向发射一定强度的激光。

所有的反射面都不是完美的,每个反射面有一个折损系数 a ,当强度为 I 的光线照射上去时,反射光线的强度会变成 aI 。为了便于处理,你可以认为所有反射面的材质均不算太好也不算太糟,因此所有的 a 均在 0.2∼0.8 的范围内。

在一些超高速摄影问题中,有时甚至连光速都要考虑在内。在这个问题中,我们不妨假设激光在 1 单位时间内恰好移动 1 单位距离。然而,超高速摄影带来的往往是采样精度的损失,因此对于一束激光,最终采样到的光线强度都是向下取整后的数值。特别地,当一束激光的强度小于 1 时,认为其已经完全耗散。

问题的最开始,平面上没有反射面也没有光源。接下来你需要处理若干个操作,每个操作形如:

1 x1 y1 x2 y2 a:在平面上插入一个分别以 (x1,y1) 和 (x2,y2) 为端点,反射系数为 a 的反射面,保证反射面与坐标轴成 45 度角摆放,且不与先前已经存在、且还没有被删除的反射面在非端点处相交;另外受到渲染效率的影响,问题中的所有反射面的总长度(可以理解为所有的 |x1−x2| 之和)不会太大。

2 k:删除第 k 个操作插入的反射面,保证第 k 个操作发生在当前操作之前且为一个插入操作,且这个反射面还没有被删除;

3 x y d I t:在 (x,y) 位置放置一个光源,发射光线的方向为 d ,强度为 I ,求其所经 t 时刻后光线到达的坐标以及采样得到的光线强度。其中 d 的含义为:d=0 表示沿 x 坐标增加的方向,d=1 表示沿 y 坐标增加的方向,d=2 表示沿 x 坐标减小的方向,d=3 表示沿 y 坐标减小的方向。另外,保证光源不位于当前存在的某个反射面(不含端点)上。注意:如果 t 时刻后光线刚好到达某个反射面,则其强度取反射后的强度。

输入格式

从标准输入读入数据。

第 1 行,一个正整数 m 表示操作的总数量。

接下来 m 行,每行描述一个操作,格式如题目描述。

其中,除了所有的 a 和 I 以外的输入均为绝对值不超过 109 的整数,其中 k 和 t 为正整数;a 和 I 均为小数点后不超过 6 位的正实数,其中 a 在 0.2∼0.8之间, I≤109。

输出格式

输出到标准输出。

对于每个查询操作输出一行,3 个整数,形如 x y I 表示激光最终到达的位置为 (x,y) ,采样得到的光线强度为 I 。特别地,如果采样到的光线强度为 0 (即光线已耗散),你也就无需关心最终到达的坐标,而只需要输出0 0 0即可。

题目数据保证,你可以在计算时直接使用 64 位浮点数的运算和取整操作,而无需担心可能的精度误差问题。

样例输入

7
1 0 4 2 2 0.4
1 2 2 0 0 0.45
3 -1 3 0 6 5
3 1 5 3 2.4 5
3 0 2 0 3 4
2 1
3 1 5 3 2.4 5

样例输出

0 1 1
0 0 0
4 2 3
0 1 1

数据范围

测试点编号m≤特殊性质
1∼31000所有光线 t≤1000 ,所有输入坐标的绝对值 ≤1000
4∼71000
8∼10105所有光线的 t≤10
11∼13105所有 1 操作在所有 3 操作之前,且无 2 操作
14∼16105所有光线的 I=1
17∼20105

对于 100% 的数据,保证 m≤105 ,所有反射面的 |x1−x2| 之和不超过 3∗105 。

100分代码

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

#define EPS 1e-8
#define INF 0x7fffffff
#define mm0(x) memset(x,0,sizeof(x))
#define debug(a) cout<<#a<<"="<<a<<endl
typedef long long ll;

struct Node{
	double a;
	bool dir;
	int k;
};
struct Op{
	int x;
	int y;
};
vector<vector<Op>> ops(100005);
class Message{
public:
	int x;
	int y;
	double I;
	void reset(int x,int y,double I){
		this->x=x;
		this->y=y;
		this->I=I;
	}
	friend ostream& operator<<(ostream& out,const Message& m){
		out<<m.x<<" "<<m.y<<" "<<(int)m.I;
		return out;
	}
};
map<int,map<int,Node>> X;
map<int,map<int,Node>> Y;
int m,cur,id;
int x1,y1,x2,y2,k,x,y,d,t;
double a,I;
bool direction;
Node temp_node;
Message temp_message;
vector<Op> temp_vop;
void Insert(){
	direction=(x1-x2)*(y1-y2)>0?true:false;
	temp_node.a=a;
	temp_node.dir=direction;
	temp_node.k=m-cur;
	temp_vop.clear();
	if(direction){
		int xx=min(x1,x2);
		int yy=min(y1,y2);
		for(int i=1;i<abs(x1-x2);i++){
			X[xx+i][yy+i]=temp_node;
			Y[yy+i][xx+i]=temp_node;
			temp_vop.push_back({xx+i,yy+i});
		}
	}else{
		int xx=min(x1,x2);
		int yy=max(y1,y2);
		for(int i=1;i<abs(x1-x2);i++){
			X[xx+i][yy-i]=temp_node;
			Y[yy-i][xx+i]=temp_node;
			temp_vop.push_back({xx+i,yy-i});
		}
	}
	ops[m-cur]=temp_vop;
	return;
}
void Erase(){
	temp_vop=ops[k];
	for(auto g:temp_vop){
		X[g.x].erase(g.y);
		Y[g.y].erase(g.x);
	}
}
void Reflect(){
	auto xi=X.find(x);
	auto xj=xi->second.begin();
	auto yi=Y.find(y);
	auto yj=yi->second.begin();
	while(t>0){
		xi=X.find(x);
		xj=xi->second.begin();
		yi=Y.find(y);
		yj=yi->second.begin();
//		debug(d);
		switch(d){
			case 0:
				if(yi!=Y.end()){
					yj=yi->second.upper_bound(x);
					if(yj!=yi->second.end()){
						if(t>=yj->first-x){
							t-=yj->first-x;
							x=yj->first;
							I*=yj->second.a;
							if(I<1){
								x=0;
								y=0;
								I=0;
								t=0;
							}
							if(yj->second.dir){
								d=1;
							}else{
								d=3;
							}
						}else{
							x+=t;
							t=0; 
						}
					}else{
						x+=t;
						t=0;
					}
				}else{
					x+=t;
					t=0;
				}
				break;
			case 1:
				if(xi!=X.end()){
					xj=xi->second.upper_bound(y);
					if(xj!=xi->second.end()){
						if(t>=xj->first-y){
							t-=xj->first-y;
							y=xj->first;
							I*=xj->second.a;
							if(I<1){
								x=0;
								y=0;
								I=0;
								t=0;
							}
							if(xj->second.dir){
								d=0;
							}else{
								d=2;
							}
						}else{
							y+=t;
							t=0; 
						}
					}else{
						y+=t;
						t=0;
					}
				}else{
					y+=t;
					t=0;
				}
				break;
			case 2:
				if(yi!=Y.end()){
					yj=yi->second.lower_bound(x);
					if(yj!=yi->second.begin()){
						yj--;
						if(t>=x-yj->first){
							t-=x-yj->first;
							x=yj->first;
							I*=yj->second.a;
							if(I<1){
								x=0;
								y=0;
								I=0;
								t=0;
							}
							if(yj->second.dir){
								d=3;
							}else{
								d=1;
							}
						}else{
							x-=t;
							t=0; 
						}
					}else{
						x-=t;
						t=0;
					}
				}else{
					x-=t;
					t=0;
				}
				break;
			case 3:
				if(xi!=X.end()){
					xj=xi->second.lower_bound(y);
					if(xj!=xi->second.begin()){
						xj--;
						if(t>=y-xj->first){
							t-=y-xj->first;
							y=xj->first;
							I*=xj->second.a;
							if(I<1){
								x=0;
								y=0;
								I=0;
								t=0;
							}
							if(xj->second.dir){
								d=2;
							}else{
								d=0;
							}
						}else{
							y-=t;
							t=0; 
						}
					}else{
						y-=t;
						t=0;
					}
				}else{
					y-=t;
					t=0;
				}
				break;
		}
	}
	temp_message.reset(x,y,I);
	cout<<temp_message<<endl;
	return;
}
int main() {
	//提高cin,cout的速度 
	ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
//	freopen("4.in","r",stdin);
//	freopen("4.ans","w",stdout);
	cin>>m;
	cur=m;
	while(cur--){
		cin>>id;
		switch(id){
			case 1:
				cin>>x1>>y1>>x2>>y2>>a;
				Insert();
				break;
			case 2:
				cin>>k;
				Erase();
				break;
			case 3:
				cin>>x>>y>>d>>I>>t;
				Reflect();
				break;
		}
	}
//	fclose(stdin); 
//	fclose(stdout);
	return 0;
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值