XVII Open Cup Eastern Grand Prix - J Votter and Paul De Mort

题目链接:http://opencup.ru/files/och/gp4/problems1-e.pdf

题目大意:平面上有 n 个点和一个六芒星区域,六芒星区域可以看作是一个中心位于原点、边长为 3L 的等边三角形和它关于原点旋转 180 度后的三角形取并集得到的区域,现在要问能否把这个六芒星区域关于原点旋转一定角度,使得 n 个点中任意两个点的连线都被六芒星区域覆盖。六芒星如下所示。

数据范围: 1n1000,1L2106 , 每个点的坐标绝对值在 2106 以内,有多组数据, n 之和不超过 3.5105

题解:

首先需要注意到这个问题是关于极角的问题(选一个旋转角度使得所有线段被覆盖),将六芒星区域按照到原点的距离划分成许多的曲线微元会方便处理后续的问题。如下图所示,对于一个给定的距离,我们将可行的极角区间标为绿色,不可行的极角区间标为红色,其中距离不超过 L 时所有极角都是可行的,而距离不小于 3L 时所有极角都是不可行的。

结合上面的转化,不难发现题目中所提到的 (n2) 对线段是不需要全部都检查的。考虑这 n 个点组成的凸包,如果凸包上的线段是可行的,那么凸包内部的线段也都将是可行的;如果存在一条线段不可行,那么凸包上的线段也必将存在不可行的情况。

现在依次考虑凸包上的每条线段。这条线段上不能有距离原点不小于 3L 的点,否则不可行。这条线段上距离原点不大于 L 的点不影响极角的选择,去掉这样的点之后线段会变成至多两个只在上图中绿色圆圈外部、红色圆圈内部的线段。

考虑新的线段上每个点对应的可行极角区间,每个点都会有 6 个可行的区间,我们需要求的都是线段上所有点的可行区间的交集,只有极角在这 6 个可行区间里的某一个时,这条线段才能被覆盖。

注意到一个点对应的可行区间之和这个点的极角、这个点到原点的距离有关,因此一条线段上的点所对应的可行区间是线性变化的,只需要知道端点对应的可行区间,就可以知道所有点的可行区间的交集。

剩下的事情就是写个扫描线扫一圈极角了,极角可以只扫 [0,π/3) 这一段,时间复杂度 O(nlogn)

似乎没遇到精度问题,大概是数据非常的水,至少给出的参考程序就是错的,敢写敢过吧。

代码:

#include <bits/stdc++.h>
using namespace std;
typedef double DB;
const int maxn = 1001;
const DB pi = acos(-1.0), eps = 1e-14;
int t, n, L, m, ban;
inline int sgn(DB x) {
    return (x > eps) - (x < -eps);
}
struct Point {
    DB x, y;
    void read() {
        int _x, _y;
        scanf("%d%d", &_x, &_y);
        x = _x;
        y = _y;
    }
    bool operator < (Point const &t) const {
        int tp = sgn(x - t.x);
        return tp < 0 || (!tp && sgn(y - t.y) < 0);
    }
    Point operator - (Point const &t) const {
        return (Point){x - t.x, y - t.y};
    }
    Point operator + (Point const &t) const {
        return (Point){x + t.x, y + t.y};
    }
    Point operator * (DB const &t) const {
        return (Point){t * x, t * y};
    }
    DB det(Point const &t) const {
        return x * t.y - y * t.x;
    }
    DB dot(Point const &t) const {
        return x * t.x + y * t.y;
    }
    DB len2() const {
        return dot(*this);
    }
} p[maxn], q[maxn];
Point LineLineIntersection(Point p1, Point p2, Point q1, Point q2) {
    return p1 + (p2 - p1) * ((q2 - q1).det(p1 - q1) / (p2 - p1).det(q2 - q1));
}
int CircleLineIntersection(Point o, DB r, Point p1, Point p2, Point &q1, Point &q2) {
    Point v = p2 - p1;
    Point p = LineLineIntersection(o, (Point){o.x + v.y, o.y - v.x}, p1, p2);
    int tp = sgn(r * r - (p - o).len2());
    if(tp < 0)
        return 0;
    if(tp == 0) {
        q1 = q2 = p;
        return 1;
    }
    DB k = sqrtl((r * r - (p - o).len2()) / (p2 - p1).len2());
    q1 = p - v * k;
    q2 = p + v * k;
    return 2;
}
struct Event {
    DB tim;
    int typ;
    bool operator < (Event const &t) const {
        int tp = sgn(tim - t.tim);
        return tp < 0 || (!tp && typ < t.typ);
    }
} e[maxn * 28 + 1];
bool addEvent(Point p1, Point p2) { // L <= |p|, |q| <= sqrt(3) L
    DB u1 = atan2(p1.y, p1.x), v1 = asin(sqrtl(3LL * L * L / p1.len2() / 4)) - pi / 6;
    DB u2 = atan2(p2.y, p2.x), v2 = asin(sqrtl(3LL * L * L / p2.len2() / 4)) - pi / 6;
    if(sgn(u1 - u2) < 0) {
        swap(u1, u2);
        swap(v1, v2);
    }
    if(sgn(u1 - u2 - pi) > 0) {
        u2 += pi * 2;
        swap(u1, u2);
        swap(v1, v2);
    }
    DB L = max(u1 - v1, u2 - v2), R = min(u1 + v1, u2 + v2);
    if(sgn(L - R) > 0)
        return 0;
//  printf("(%.4f, %.4f) -> (%.4f, %.4f): [%.10f, %.10f]\n", p1.x, p1.y, p2.x, p2.y, L, R);
    if(sgn(R) < 0) {
        L += pi * 2;
        R += pi * 2;
    }
    bool vis = 0;
    for(int i = 0; i < 6; ++i, L += pi / 3, R += pi / 3) {
        if(sgn(R - pi * 2) >= 0) {
            L -= pi * 2;
            R -= pi * 2;
        }
        if(sgn(L) <= 0) {
            vis = 1;
            e[m++] = (Event){R, 1};
            e[m++] = (Event){L + pi * 2, -1};
        } else {
            e[m++] = (Event){L, -1};
            e[m++] = (Event){R, 1};
        }
    }
    ban += !vis;
    return 1;
}
int main() {
    scanf("%d", &t);
    while(t--) {
        scanf("%d%d", &m, &L);
        bool chk = 1;
        for(int i = 0; chk && i < m; ++i) {
            q[i].read();
            chk &= sgn(q[i].len2() - 3LL * L * L) <= 0;
        }
        if(!chk) {
            puts("Change");
            continue;
        }
        sort(q, q + m);
        n = 0;
        for(int i = 0; i < m; ++i) {
            for( ; n > 1 && sgn((p[n - 1] - p[n - 2]).det(q[i] - p[n - 2])) <= 0; --n);
            p[n++] = q[i];
        }
        for(int i = m - 1, tp = n; i >= 0; --i) {
            for( ; n > tp && sgn((p[n - 1] - p[n - 2]).det(q[i] - p[n - 2])) <= 0; --n);
            p[n++] = q[i];
        }
        n -= n > 1;
        m = ban = 0;
        for(int i = n - 1, j = 0, si = sgn(p[i].len2() - 1LL * L * L), sj; chk && j < n; i = j++, si = sj) {
            sj = sgn(p[j].len2() - 1LL * L * L);
            if(si <= 0 && sj <= 0)
                continue;
            Point p1, p2;
            int cnt = CircleLineIntersection((Point){0, 0}, L, p[i], p[j], p1, p2);
            if(cnt == 2 && sgn((p2 - p1).dot(p[j] - p[i])) < 0)
                swap(p1, p2);
            if(cnt == 2 && sgn((p2 - p[i]).dot(p2 - p[j])) > 0)
                --cnt;
            if(cnt >= 1 && sgn((p1 - p[i]).dot(p1 - p[j])) > 0) {
                p1 = p2;
                --cnt;
            }
            if(cnt <= 1) {
                chk &= addEvent(si < 0 ? p1 : p[i], sj < 0 ? p1 : p[j]);
            } else {
                if(si > 0)
                    chk &= addEvent(p[i], p1);
                if(sj > 0)
                    chk &= addEvent(p2, p[j]);
            }
        }
        if(!chk) {
            puts("Change");
            continue;
        }
        chk = !ban;
        sort(e, e + m);
        for(int i = 0; !chk && i < m; ++i) {
            ban += e[i].typ;
            chk |= !ban;
        }
        puts(chk ? "Cast" : "Change");
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/skywalkert/article/details/80170193
DE