如果原点能被一个光源照到,那么这两个点之间一定没有任何球。我们可以通过三分距离来确定某线段和球是否有交点。
注意到m非常小,于是我们可以枚举原点被哪些光源照到。由于(O(3^{m}*n))会超时,我们可以对每个光源需要移走那些球进行压位。
复杂度(O(3^{m}*n / w))
跑的有点慢啊……
#include <bits/stdc++.h> #define UI unsigned int #define N 2100 #define eps 0.000001 using namespace std; int n, m, r; struct node { double x, y, z; } light[N], tar; struct circle { node O; double r; } ball[N]; double power[N]; void get(int t, int &d, int &u) { u = t >> 5; d = t ^ (u << 5); } double dist(node A, node B) { return pow(pow(A.x - B.x, 2) + pow(A.y - B.y, 2) + pow(A.z - B.z, 2), 0.5); } node get(node A, node B, double d) { node nw; nw.x = (B.x - A.x) * d + A.x; nw.y = (B.y - A.y) * d + A.y; nw.z = (B.z - A.z) * d + A.z; return nw; } UI shel[20][100]; UI nowi[40000][100]; int st[80000]; int ok[40000]; double ans; int main() { for (int i = 0; i < (1 << 16); ++ i) for (int j = 0; j < 16; ++ j) if (i & (1 << j)) st[i] ++; while (scanf("%d%d%d", &n, &m, &r), n + m + r) { ans = 0; for (int i = 0; i < n; ++ i) scanf("%lf%lf%lf%lf", &ball[i].O.x, &ball[i].O.y, &ball[i].O.z, &ball[i].r); for (int i = 0; i < m; ++ i) scanf("%lf%lf%lf%lf", &light[i].x, &light[i].y, &light[i].z, &power[i]); scanf("%lf%lf%lf", &tar.x, &tar.y, &tar.z); for (int i = 0; i < m; ++ i) power[i] /= pow(dist(tar, light[i]), 2); for (int i = 0; i < m; ++ i) for (int j = 0; j < n / 32 + 1; ++ j) shel[i][j] = 0; for (int i = 0; i < m; ++ i) for (int j = 0; j < n; ++ j) { if (dist(ball[j].O, tar) < ball[j].r && dist(ball[j].O, light[i]) < ball[j].r) continue; int d, u; get(j, d, u); double l = 0, r = 1; while (r - l > eps) { double m1 = (l + l + r) / 3, m2 = (l + r + r) / 3; node p1 = get(tar, light[i], m1), p2 = get(tar, light[i], m2); if (dist(ball[j].O, p1) < dist(ball[j].O, p2)) { if (dist(ball[j].O, p1) < ball[j].r) shel[i][u] |= 1 << d; r = m2; } else { if (dist(ball[j].O, p2) < ball[j].r) shel[i][u] |= 1 << d; l = m1; } } } for (int i = 1; i < (1 << m); ++ i) { ok[i] = 1; for (int j = 0; (1 << j) < i; j ++) if (i & (1 << j)) if (!ok[i ^ (1 << j)]) ok[i] = 0; if (!ok[i]) continue; for (int j = 0; (1 << j) <= i; j ++) if (i & (1 << j)) { int s = 0; for (int k = 0; k < n / 32 + 1; ++ k) { nowi[i][k] = nowi[i ^ (1 << j)][k] | shel[j][k]; s += st[nowi[i][k] >> 16] + st[nowi[i][k] ^ ((nowi[i][k] >> 16) << 16)]; } ok[i] = (s <= r); break; } if (ok[i]) { double nowans = 0; for (int j = 0; j < m; ++ j) if (i & (1 << j)) nowans += power[j]; ans = max(ans, nowans); } } printf("%.6lf ", ans); } }