bzoj4691: Let There Be Light

如果原点能被一个光源照到,那么这两个点之间一定没有任何球。我们可以通过三分距离来确定某线段和球是否有交点。

注意到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\n", ans);
    }
}

 

posted @ 2016-08-30 15:15  AwD!  阅读(256)  评论(1)    收藏  举报