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

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

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

数据范围: 1 ≤ n ≤ 1000 , 1 ≤ L ≤ 2 ⋅ 1 0 6 1 \leq n \leq 1000, 1 \leq L \leq 2 \cdot 10^6 1n1000,1L2106, 每个点的坐标绝对值在 2 ⋅ 1 0 6 2 \cdot 10^6 2106 以内,有多组数据, n n n 之和不超过 3.5 ⋅ 1 0 5 3.5 \cdot 10^5 3.5105

题解:

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

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

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

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

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

剩下的事情就是写个扫描线扫一圈极角了,极角可以只扫 [ 0 , π / 3 ) [0, \pi/3) [0,π/3) 这一段,时间复杂度 O ( n log ⁡ n ) O(n \log n) 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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
软件名称 福昕pdf阅读器 版权所有 福昕软件(www.fuxinsoftware.com.cn) 更新日期 2009-03-10 软件大小 3.40 MB / 3481.6KB 版本 3.0 Build 1506 操作系统 Windows Me/2000/XP/2003/Vista 客户端地址http://mirrors.foxitsoftware.com/pub/foxit/reader/desktop/win/3.x/3.0/chs/FoxitReader30_chs.zip 软件简介 Foxit Reader(福昕阅读器)是一款免费的PDF文档阅读器和打印器,具有令人难以置信的小巧体积、快捷的启动速度和丰富的功能。新发布的3.0版本支持Windows Me/2000/XP/2003/Vista操作系统,其核心技术与PDF标准版1.7完全兼容。 该版本修复了三个重要安全漏洞,这些漏洞可能会导致应用程序崩溃,并且可能导致黑客控制受影响的系统。在发现漏洞后,我们非常重视这些问题并进行仔细研究。仅用了两三天,我们的技术部门就快速有效地解决了所有安全问题。现在,福昕阅读器更加的稳定易用。 已修复的漏洞包括: • 修复基于堆栈的缓存溢出问题 福昕软件对PDF文件的各种操作会跟不同的触发器相关联。诸如打开或执行文档、打开网络链接等操作使用了一个过长的文件名,在触发器条件允许的情况下,将会引发一个基于堆栈的缓存溢出问题。 • 修复安全授权旁路问题 诸如打开或执行文档、打开网络链接等操作,在触发器条件满足的情形下,福昕阅读器将执行文档创建者的指令,而不弹出一个对话框供用户确认是否执行,而有些文档创建者的执行指令存在风险。 • 修复JBIG2符号字典处理过程中一个未初始化问题 当解码JBIG2符号字典段时,一个已分配的和导出符号个数(SDNUMEXSYMS)相等的32-bit元素数组在新符号个数(SDNUMNEWSYMS)为0的情况下未初始化. 该数组随后被访问, 其未初始化的值随后作为指针来读取内存并进行调用。 福昕软件中文官网:www.fuxinsoftware.com.cn 福昕软件英文官网:www.foxitsoftware.com

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值