自己打印用,复制了大佬们的代码,致歉
4512. 光线追踪
光线追踪是计算机图形学领域的一个重要算法,其原理是追踪一束从光源发出的光,经过不同的反射面,最终到达摄像机处的过程。
在这道问题中,你需要实现一段程序来处理一个简易的光线追踪模型。
在平面中有一些反射面,为方便起见,我们设这些反射面都是线段,与坐标轴成 4545 度角摆放,且两个端点的坐标均为整数。
为进一步简化问题,我们假设所有的反射表面都是镜面反射。
任何一束光线照射到反射面上(为避免讨论,假设反射面不含端点)时,都会改变方向为相应的镜面反射方向。
注意,反射面的两侧都可以反射光线。
平面中还有一些激光光源,每个光源位于一个坐标为整数的点上,会向某个水平或竖直的方向发射一定强度的激光。
所有的反射面都不是完美的,每个反射面有一个折损系数 a𝑎,当强度为 I𝐼 的光线照射上去时,反射光线的强度会变成 aI𝑎𝐼。
为了便于处理,你可以认为所有反射面的材质均不算太好也不算太糟,因此所有的 a𝑎 均在 0.2∼0.80.2∼0.8 的范围内。
在一些超高速摄影问题中,有时甚至连光速都要考虑在内。
在这个问题中,我们不妨假设激光在 11 单位时间内恰好移动 11 单位距离。
然而,超高速摄影带来的往往是采样精度的损失,因此对于一束激光,最终采样到的光线强度都是向下取整后的数值。
特别地,当一束激光的强度小于 11 时,认为其已经完全耗散。
问题的最开始,平面上没有反射面也没有光源。
接下来你需要处理若干个操作,每个操作形如:
1 x1 y1 x2 y2 a
:在平面上插入一个分别以 (x1,y1)(𝑥1,𝑦1) 和 (x2,y2)(𝑥2,𝑦2) 为端点,反射系数为 a𝑎 的反射面,保证反射面与坐标轴成 4545 度角摆放,且不与先前已经存在、且还没有被删除的反射面在非端点处相交;另外受到渲染效率的影响,问题中的所有反射面的总长度(可以理解为所有的 |x1−x2||𝑥1−𝑥2| 之和)不会太大。2 k
:删除第 k𝑘 个操作插入的反射面,保证第 k𝑘 个操作发生在当前操作之前且为一个插入操作,且这个反射面还没有被删除;3 x y d I t
:在 (x,y)(𝑥,𝑦) 位置放置一个光源,发射光线的方向为 d𝑑,强度为 I𝐼,求其所经 t𝑡 时刻后光线到达的坐标以及采样得到的光线强度。其中 d𝑑 的含义为:d=0𝑑=0 表示沿 x𝑥 坐标增加的方向,d=1𝑑=1 表示沿 y𝑦 坐标增加的方向,d=2𝑑=2 表示沿 x𝑥 坐标减小的方向,d=3𝑑=3 表示沿 y𝑦 坐标减小的方向。另外,保证光源不位于当前存在的某个反射面(不含端点)上。注意:如果 t𝑡 时刻后光线刚好到达某个反射面,则其强度取反射后的强度。
输入格式
第 11 行,一个正整数 m𝑚 表示操作的总数量。
接下来 m𝑚 行,每行描述一个操作,格式如题目描述。
其中,除了所有的 a𝑎 和 I𝐼 以外的输入均为绝对值不超过 109109 的整数,其中 k𝑘 和 t𝑡 为正整数;a𝑎 和 I𝐼 均为小数点后不超过 66 位的正实数,其中 a𝑎 在 0.2∼0.80.2∼0.8 之间,I≤109𝐼≤109。
输出格式
对于每个查询操作输出一行,33 个整数,形如 x y I
表示激光最终到达的位置为 (x,y)(𝑥,𝑦),采样得到的光线强度为 I𝐼。特别地,如果采样到的光线强度为 00 (即光线已耗散),你也就无需关心最终到达的坐标,而只需要输出 0 0 0
即可。
题目数据保证,你可以在计算时直接使用 6464 位浮点数的运算和取整操作,而无需担心可能的精度误差问题。
数据范围
测试点编号 | m≤𝑚≤ | 特殊性质 |
---|---|---|
1∼31∼3 | 10001000 | 所有光线的 t≤1000𝑡≤1000,所有输入坐标的绝对值 ≤1000≤1000 |
4∼74∼7 | 10001000 | 无 |
8∼108∼10 | 105105 | 所有光线的 t≤10𝑡≤10 |
11∼1311∼13 | 105105 | 所有 11 操作在所有 33 操作之前,且无 22 操作 |
14∼1614∼16 | 105105 | 所有光线的 I≤1𝐼≤1 |
17∼2017∼20 | 105105 | 无 |
对于 100%100% 的数据,保证 m≤105𝑚≤105,所有反射面的 |x1−x2||𝑥1−𝑥2| 之和不超过 3×1053×105。
输入样例:
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
#include<iostream>
#include<map>
#include<cmath>
using namespace std;
enum class LightDir {
X_POS = 0,
Y_POS = 1,
X_NEG = 2,
Y_NEG = 3
};
int dx[] = { 1,0,-1,0 }, dy[] = {0,1,0,-1};
int sgn(int x) {
if (x > 0) return 1;
if (x < 0) return -1;
return 0;
}
struct point {
int x, y;
bool operator==(const point& p) const {
return x == p.x && y == p.y;
}
};
typedef pair<point, int> point_with_int;
const point NULL_POINT = { 2e9, 2e9 };
const point_with_int NULL_PD = { NULL_POINT, -1 };
struct mirror {
int x1, y1, x2, y2, k, id;
double a;
void read(int id) {
cin >> x1 >> y1 >> x2 >> y2 >> a;
if (x1 > x2) {
swap(x1, x2); swap(y1, y2);
}
k = (y2 - y1) / (x2 - x1);
this->id = id;
}
}mirrors[200001];
class space {
/*
* psx = 所有镜面整点按x方向排序;
* key存放所有点的y值,value递增存储对应x坐标和倾斜方向
* psy = 所有镜面整点按y方向排序;
* key存放所有点的x值,value递增存储对应y坐标和倾斜方向
*/
map<int, map<int,int> > psx, psy;
void add_point(int x, int y, int id) {
auto it = psx.find(y);
if (it == psx.end()) {
map<int, int> add;
add.insert({ x,id }); psx.insert({ y, add });
}
else {
it->second.insert({ x,id });
}
it = psy.find(x);
if (it == psy.end()) {
map<int, int> add;
add.insert({ y,id }); psy.insert({ x, add });
}
else {
it->second.insert({ y,id });
}
}
void del_point(int x, int y) {
psx[y].erase(x);
psy[x].erase(y);
}
public:
void add_mirror(mirror m) {
for (int x = m.x1 + 1, y = m.y1 + m.k; x < m.x2; x++, y += m.k) {
add_point(x, y, m.id);
}
}
void del_mirror(mirror m) {
for (int x = m.x1 + 1, y = m.y1 + m.k; x < m.x2; x++, y += m.k) {
del_point(x, y);
}
}
//返回反射点和反射面编号
point_with_int find_nearst_reflect_point(point p, LightDir d) {
if (d == LightDir::X_POS || d == LightDir::X_NEG) {
auto it = psx.find(p.y);
if (it == psx.end()) return NULL_PD;
map<int, int>::iterator it2;
if (d == LightDir::X_POS) {
it2 = it->second.upper_bound(p.x);
if (it2 == it->second.end()) return NULL_PD;
}
else {
it2 = it->second.lower_bound(p.x);
if (it2 == it->second.begin()) return NULL_PD;
--it2;//技巧:lower_bound的前一个就是第一个比p.x小的数
}
return { {it2->first,p.y}, it2->second };
}
else {
auto it = psy.find(p.x);
if (it == psy.end()) return NULL_PD;
map<int, int>::iterator it2;
if (d == LightDir::Y_POS) {
it2 = it->second.upper_bound(p.y);
if (it2 == it->second.end()) return NULL_PD;
}
else {
it2 = it->second.lower_bound(p.y);
if (it2 == it->second.begin()) return NULL_PD;
--it2;
}
return { {p.x,it2->first}, it2->second };
}
}
}instance;
LightDir next_dir(LightDir dir, int mirror_k) {
if (dir == LightDir::X_POS) {
return mirror_k == 1 ? LightDir::Y_POS : LightDir::Y_NEG;
}
else if (dir == LightDir::X_NEG) {
return mirror_k == -1 ? LightDir::Y_POS : LightDir::Y_NEG;
}
else if (dir == LightDir::Y_POS) {
return mirror_k == 1 ? LightDir::X_POS : LightDir::X_NEG;
}
else {//(dir == LightDir::Y_NEG)
return mirror_k == -1 ? LightDir::X_POS : LightDir::X_NEG;
}
}
pair<point, int> test_source(point p, LightDir d, double I, int T) {
do {
point_with_int ret = instance.find_nearst_reflect_point(p, d);
point p2 = ret.first; int id = ret.second;
int dist = abs(p.x - p2.x) + abs(p.y - p2.y);
if (p2 == NULL_POINT || dist > T) {
p.x += dx[(int)d] * T;
p.y += dy[(int)d] * T;
break;
}
p = p2;
d = next_dir(d, mirrors[id].k);
I = I * mirrors[id].a;
if (I < 1.0) {
return { {0,0},0 };
}
T -= dist;
} while (T > 0);
return { p,(int)I };
}
int main() {
ios::sync_with_stdio(false);
int m;
cin >> m;
for (int i = 1; i <= m; i++) {
int op; cin >> op;
if (op == 1) {
mirrors[i].read(i);
instance.add_mirror(mirrors[i]);
}
else if (op == 2) {
int k; cin >> k;
instance.del_mirror(mirrors[k]);
}
else {
int x, y, d, t;
double I;
cin >> x >> y >> d >> I >> t;
auto ans = test_source({ x,y }, (LightDir)d, I, t);
cout << ans.first.x << ' ' << ans.first.y << ' ' << ans.second << endl;
}
}
return 0;
}
4008. 脉冲神经网络
在本题中,你需要实现一个 SNN(spiking neural network,脉冲神经网络)的模拟器。
一个 SNN 由以下几部分组成:
- 神经元:按照一定的公式更新内部状态,接受脉冲并可以发放脉冲
- 脉冲源:在特定的时间发放脉冲
- 突触:连接神经元-神经元或者脉冲源-神经元,负责传递脉冲
神经元会按照一定的规则更新自己的内部状态。
本题中,我们对时间进行离散化处理,即设置一个时间间隔 ΔtΔ𝑡,仅考虑时间间隔整数倍的时刻 t=kΔt(k∈Z+)𝑡=𝑘Δ𝑡(𝑘∈𝑍+),按照下面的公式,从 k−1𝑘−1 时刻的取值计算 k𝑘 时刻的变量的取值:
vk=vk−1+Δt(0.04v2k−1+5vk−1+140−uk−1)+Ik𝑣𝑘=𝑣𝑘−1+Δ𝑡(0.04𝑣𝑘−12+5𝑣𝑘−1+140−𝑢𝑘−1)+𝐼𝑘
uk=uk−1+Δta(bvk−1−uk−1)𝑢𝑘=𝑢𝑘−1+Δ𝑡𝑎(𝑏𝑣𝑘−1−𝑢𝑘−1)
其中 v𝑣 和 u𝑢 是神经元内部的变量,会随着时间而变化,a𝑎 和 b𝑏 是常量,不会随着时间变化;其中 Ik𝐼𝑘 表示该神经元在 k𝑘 时刻接受到的所有脉冲输入的强度之和,如果没有接受到脉冲,那么 Ik=0𝐼𝑘=0。
当进行上面的计算后,如果满足 vk≥30𝑣𝑘≥30,神经元会发放一个脉冲,脉冲经过突触传播到其他神经元;同时,vk𝑣𝑘 设为 c𝑐 并且 uk𝑢𝑘 设为 uk+d𝑢𝑘+𝑑,其中 c𝑐 和 d𝑑 也是常量。
图 11 展示了一个神经元 v𝑣 变量随时间变化的曲线。
图 11: 神经元 v𝑣 变量随时间变化的曲线
突触表示的是神经元-神经元、脉冲源-神经元的连接关系,包含一个入结点和一个出结点(可能出现自环和重边)。
当突触的入结点(神经元或者脉冲源)在 k𝑘 时刻发放一个脉冲,那么在传播延迟 D(D>0)𝐷(𝐷>0) 个时刻以后,也就是在 k+D𝑘+𝐷 时刻突触的出结点(神经元)会接受到一个强度为 w𝑤 的脉冲。
脉冲源在每个时刻以一定的概率发放一个脉冲,为了模拟这个过程,每个脉冲源有一个参数 0<r≤327670<𝑟≤32767,并统一采用以下的伪随机函数:
C++ 版本:
static unsigned long next = 1;
/* RAND_MAX assumed to be 32767 */
int myrand(void) {
next = next * 1103515245 + 12345;
return((unsigned)(next/65536) % 32768);
}
Python 版本:
next = 1
def myrand():
global next
next = (next * 1103515245 + 12345) % (2 ** 64)
return (next // 65536) % 32768
Java 版本:
long next = 1;
int myrand() {
next = next * 1103515245 + 12345;
return (int)((Long.divideUnsigned(next, 65536)) % 32768);
}
在每个时间刻,按照编号顺序从小到大,每个脉冲源调用一次上述的伪随机函数,当 r>myrand()𝑟>myrand() 时,在当前时间刻发放一次脉冲,并通过突触传播到神经元。
进行仿真的时候,已知 00 时刻各个神经元的状态,从 11 时刻开始按照上述规则进行计算,直到完成 T𝑇 时刻的计算,再输出 T𝑇 时刻神经元的 v𝑣 值和发放的脉冲次数分别的最小值和最大值。
规定输入数据中结点按如下方式顺序编号:[0,N−1][0,𝑁−1] 为神经元的编号,[N,N+P−1][𝑁,𝑁+𝑃−1] 为脉冲源的编号。
代码中请使用双精度浮点类型。
输入格式
输入的第一行包括四个以空格分隔的正整数 N𝑁 S𝑆 P𝑃 T𝑇,表示一共有 N𝑁 个神经元,S𝑆 个突触和 P𝑃 个脉冲源,输出时间刻 T𝑇 时神经元的 v𝑣 值。
输入的第二行是一个正实数 ΔtΔ𝑡,表示时间间隔。
输入接下来的若干行,每行有以空格分隔的一个正整数 RN𝑅𝑁 和六个实数 v𝑣 u𝑢 a𝑎 b𝑏 c𝑐 d𝑑,按顺序每一行对应 RN𝑅𝑁 个具有相同初始状态和常量的神经元:其中 v𝑣 u𝑢 表示神经元在时刻 00 时的变量取值;a𝑎 b𝑏 c𝑐 d𝑑 为该神经元微分方程里的四个常量。保证所有的 RN𝑅𝑁 加起来等于 N𝑁。它们从前向后按编号顺序描述神经元,每行对应一段连续编号的神经元的信息。
输入接下来的 P𝑃 行,每行是一个正整数 r𝑟,按顺序每一行对应一个脉冲源的 r𝑟 参数。
输入接下来的 S𝑆 行,每行有以空格分隔的两个整数 s(0≤s<N+P)𝑠(0≤𝑠<𝑁+𝑃)、t(0≤t<N)𝑡(0≤𝑡<𝑁) 、一个实数 w(w≥0)𝑤(𝑤≥0) 和一个正整数 D𝐷,其中 s𝑠 和 t𝑡 分别是入结点和出结点的编号;w𝑤 和 D𝐷 分别表示脉冲强度和传播延迟。
输出格式
输出共有两行,第一行由两个近似保留 33 位小数的实数组成,分别是所有神经元在时刻 T𝑇 时变量 v𝑣 的取值的最小值和最大值。
第二行由两个整数组成,分别是所有神经元在整个模拟过程中发放脉冲次数的最小值和最大值。
只要按照题目要求正确实现就能通过,不会因为计算精度的问题而得到错误答案。
数据范围
数据共 33 种类型:
- 类型 11 满足,1≤T,N,S,P,D≤1001≤𝑇,𝑁,𝑆,𝑃,𝐷≤100。
- 类型 22 满足,1≤T,N,S,P,D≤10001≤𝑇,𝑁,𝑆,𝑃,𝐷≤1000。
- 类型 33 满足,1≤T≤1051≤𝑇≤105,1≤N,S,P≤10001≤𝑁,𝑆,𝑃≤1000,1≤D≤101≤𝐷≤10。
输入样例1:
1 1 1 10
0.1
1 -70.0 -14.0 0.02 0.2 -65.0 2.0
30000
1 0 30.0 2
输出样例1:
-35.608 -35.608
2 2
样例1解释
该样例有 11 个神经元、11 个突触和 11 个脉冲源,时间间隔 Δt=0.1Δ𝑡=0.1。
唯一的脉冲源通过脉冲强度为 30.030.0、传播延迟为 22 的突触传播到唯一的神经元。
该样例一共进行 1010 个时间步的模拟,随机数生成器生成 1010 次随机数如下:
16838
5758
10113
17515
31051
5627
23010
7419
16212
4086
因此唯一的脉冲源在时刻 1−41−4 和 6−106−10 发放脉冲。在时间刻从 11 到 1010 时,唯一的神经元的 v𝑣 取值分别为:
-70.000
-70.000
-40.000
-8.200
-65.000
-35.404
-32.895
0.181
-65.000
-35.608
该神经元在时刻 55 和时刻 99 发放,最终得到的 v=−35.608𝑣=−35.608。
输入样例2:
2 4 2 10
0.1
1 -70.0 -14.0 0.02 0.2 -65.0 2.0
1 -69.0 -13.0 0.04 0.1 -60.0 1.0
30000
20000
2 0 15.0 1
3 1 20.0 1
1 0 10.0 2
0 1 40.0 3
输出样例2:
-60.000 -22.092
1 2
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 2010;
const double INF = 1e8;
int n, s, p, T;
double dt;
int h[N], e[N], D[N], ne[N], idx;
double W[N], v[N], u[N], a[N], b[N], c[N], d[N];
int r[N], cnt[N];
double I[1024][N / 2];
static unsigned long _next = 1;
/* RAND_MAX assumed to be 32767 */
int myrand(void) {
_next = _next * 1103515245 + 12345;
return((unsigned)(_next/65536) % 32768);
}
void add(int a, int b, double c, int d)
{
e[idx] = b, W[idx] = c, D[idx] = d, ne[idx] = h[a], h[a] = idx ++ ;
}
int main()
{
memset(h, -1, sizeof h);
scanf("%d%d%d%d", &n, &s, &p, &T);
scanf("%lf", &dt);
for (int i = 0; i < n;)
{
int rn;
scanf("%d", &rn);
double vv, uu, aa, bb, cc, dd;
scanf("%lf%lf%lf%lf%lf%lf", &vv, &uu, &aa, &bb, &cc, &dd);
for (int j = 0; j < rn; j ++, i ++ )
{
v[i] = vv, u[i] = uu, a[i] = aa, b[i] = bb, c[i] = cc, d[i] = dd;
}
}
for (int i = n; i < n + p; i ++ ) scanf("%d", &r[i]);
int mod = 0;
while (s -- )
{
int a, b, d;
double c;
scanf("%d%d%lf%d", &a, &b, &c, &d);
add(a, b, c, d);
mod = max(mod, d + 1);
}
for (int i = 0; i < T; i ++ )
{
int t = i % mod;
for (int j = n; j < n + p; j ++ )
if (r[j] > myrand())
{
for (int k = h[j]; ~k; k = ne[k])
{
int x = e[k];
I[(t + D[k]) % mod][x] += W[k];
}
}
for (int j = 0; j < n; j ++ )
{
double vv = v[j], uu = u[j];
v[j] = vv + dt * (0.04 * vv * vv + 5 * vv + 140 - uu) + I[t][j];
u[j] = uu + dt * a[j] * (b[j] * vv - uu);
if (v[j] >= 30)
{
for (int k = h[j]; ~k; k = ne[k])
{
int x = e[k];
I[(t + D[k]) % mod][x] += W[k];
}
cnt[j] ++ ;
v[j] = c[j], u[j] += d[j];
}
}
memset(I[t], 0, sizeof I[t]);
}
double minv = INF, maxv = -INF;
int minc = INF, maxc = -INF;
for (int i = 0; i < n; i ++ )
{
minv = min(minv, v[i]);
maxv = max(maxv, v[i]);
minc = min(minc, cnt[i]);
maxc = max(maxc, cnt[i]);
}
printf("%.3lf %.3lf\n", minv, maxv);
printf("%d %d\n", minc, maxc);
return 0;
}
星际旅行 最短曲线距离