BZOJ 3199: [Sdoi2013]escape

$O(n^2)$建Voronoi图,求对偶图后BFS即可

用Canvas写了个可视化

想写增量算法和Fortune算法,可是我好菜啊orz

point的cmp写错了,调试了很久,要一直记得精度啊,用sgn函数,否则不满足偏序

半平面交抄板子都能抄错orz

此外写代码最好一气呵成,别磨叽,这东西我写了2day你敢信?

#include <iostream>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>
#include <map>
using namespace std;

typedef double DB;
const DB EPS = 1e-8;
int sgn(DB x) {
    return x < -EPS ? -1 : EPS < x;
}
struct Point {
    DB x, y;
    Point(DB x = 0, DB y = 0):x(x), y(y) {
    }
    DB dot(Point v) {
        return x * v.x + y * v.y;
    }
    DB cross(Point v) {
        return x * v.y - y * v.x;
    }
    DB norm() {
        return sqrt(this->dot(*this));
    }
    DB length() {
        return norm();
    }
    Point normalize();
    bool operator < (const Point&rhs) const {
        return sgn(x - rhs.x) < 0 || (sgn(x - rhs.x) == 0 && sgn(y - rhs.y) < 0);
    }
};
typedef Point Vector;
Point operator + (Point a, Point b) {
    return Point(a.x + b.x, a.y + b.y);
}
Point operator - (Point a, Point b) {
    return Point(a.x - b.x, a.y - b.y);
}
Point operator * (Point a, DB p) {
    return Point(a.x * p, a.y * p);
}
Point operator / (Point a, DB p) {
    return Point(a.x / p, a.y / p);
}
DB dot(Vector a, Vector b) {
    return a.dot(b);
}
DB cross(Vector a, Vector b) {
    return a.cross(b);
}
Point Point::normalize() {
    return (*this) = (*this) / this->norm();
}
bool operator == (Point a, Point b) {
    return sgn(a.x - b.x) == 0 && sgn(a.y - b.y) == 0;
}

struct Line {
    Point p;
    Vector v;
    DB ang;
    Line() {}
    Line(Point p, Vector v):p(p), v(v) {
        v.normalize();
        ang = atan2(v.y, v.x);
    }
    Point getInterPoint(Line b) {
        Line a = *this;
        return a.p + a.v * (b.p.cross(b.v) - a.p.cross(b.v)) / a.v.cross(b.v);
    }
};

bool cmpAng(const Line&a, const Line&b) {
    return a.ang < b.ang;
}
bool onLeft(Line l, Point p) {
    return l.v.cross(p - l.p) > 0;
}
vector<Point> halfplaneInter(vector<Line> l) {
    vector<Point> ret;
    sort(l.begin(), l.end(), cmpAng);
    int first, last, n = l.size();
    Point *p = new Point[n];
    Line *q = new Line[n];
    q[first = last = 0] = l[0];
    for (int i = 1; i < n; ++i) {
        while (first < last && !onLeft(l[i], p[last - 1])) --last;
        while (first < last && !onLeft(l[i], p[first])) ++first;
        q[++last] = l[i];
        if (sgn(fabs(q[last].v.cross(q[last - 1].v))) == 0) {
            --last;
            if (onLeft(q[last], l[i].p)) q[last] = l[i];
        }
        if (first < last) p[last - 1] = q[last - 1].getInterPoint(q[last]);
    }
    while (first < last && !onLeft(q[first], p[last - 1])) --last;
    if (last - first <= 1) return ret;
    p[last] = q[last].getInterPoint(q[first]);
    for (int i = first; i <= last; ++i)
        ret.push_back(p[i]);
    delete p;
    delete q;
    p = NULL, q = NULL;
    return ret;
}

namespace DCEL {
    struct HalfEdge;
    struct Face;
    struct Vertex {
        int id;
        HalfEdge *parEdge;
        Vertex() {
            parEdge = NULL;
        }
    };
    struct HalfEdge {
        HalfEdge *twin, *succ;
        int orig;
        Face *parFace;
        HalfEdge() {
            twin = succ = NULL;
            orig = 0;
            parFace = NULL;
        }
    };
    struct Face {
        int site;
        int id;
        HalfEdge *eHead;
        Face() {
            eHead = NULL;
        }
    };
};
using namespace DCEL;

struct VoronoiN2 {
    vector<Point> sites;
    vector<Point> pointPool;
    map<Point, unsigned int> pointId;
    map<pair<int, int>, HalfEdge* > conj;
    vector<Face*> faces;
    int getId(Point x) {
        if (pointId.find(x) == pointId.end()) {
            pointId.insert(make_pair(x, pointPool.size()));
            pointPool.push_back(x);
        }
        return pointId.find(x)->second;
    }
    VoronoiN2(Point*p, int n, Point a, Point c) {
        for (int i = 0; i < n; ++i)
            sites.push_back(p[i]);
        vector<Line> base;
        vector<Line> ext;
        vector<Point> vertex;
        Point b = Point(a.x, c.y), d = Point(c.x, a.y);
        base.push_back(Line(a, d - a));
        base.push_back(Line(d, c - d));
        base.push_back(Line(c, b - c));
        base.push_back(Line(b, a - b));

        Face *outerFace = new Face;
        outerFace->id = 0;
        outerFace->site = -1;
        outerFace->eHead = new HalfEdge;
        outerFace->eHead->orig = -1;
        outerFace->eHead->parFace = outerFace;
        faces.push_back(outerFace);

        for (int i = 0; i < n; ++i) {
            ext.assign(base.begin(), base.end());
            for (int j = 0; j < n; ++j) if (j != i) {
                Vector v = sites[j] - sites[i];
                ext.push_back(Line(p[i] + v / 2, Point(-v.y, v.x)));
            }
            vertex = halfplaneInter(ext);

            int m = vertex.size();
            Face *newFace = new Face;
            HalfEdge *ptn, *newPtn;
            newFace->site = getId(sites[i]);
            ptn = newFace->eHead = new HalfEdge;
            ptn->orig = getId(vertex[0]);
            ptn->parFace = newFace;

            for (int i = 1; i < m; ++i) {
                newPtn = new HalfEdge();
                newPtn->orig = getId(vertex[i]);
                newPtn->parFace = newFace;
                ptn->succ = newPtn;
                conj.insert(make_pair(make_pair(getId(vertex[i - 1]), getId(vertex[i])), ptn));
                ptn = newPtn;
            }
            ptn->succ = newFace->eHead;
            conj.insert(make_pair(make_pair(getId(vertex[m - 1]), getId(vertex[0])), ptn));
            newFace->id = faces.size();
            faces.push_back(newFace);
        }

        memset(head, -1, sizeof head);
        edgeCnt = 0;
        for (int k = 1; k < (int)faces.size(); ++k) {
            HalfEdge*i = faces[k]->eHead;
            do {
                HalfEdge*j = i->succ;
                if (i->twin == NULL) {
                    map<pair<int, int>, HalfEdge* >::iterator jt = conj.find(make_pair(j->orig, i->orig));
                    if (jt == conj.end()) {
                        i->twin = outerFace->eHead;
                    } else {
                        i->twin = jt->second;
                    }
                }
                //printf("%d -> %d\n", k, i->twin->parFace->id);
                to[edgeCnt] = i->twin->parFace->id;
                nxt[edgeCnt] = head[k];
                head[k] = edgeCnt++;
                i = j;
            } while (i != faces[k]->eHead);
        }
        //puts("");
    }
    static const int MAXN = 1000;
    static const int MAXE = 10000;
    int edgeCnt;
    int head[MAXN];
    int to[MAXE], nxt[MAXE];
    void buildDualGraph() {
    //    memset(head, -1, sizeof head);
    //    edgeCnt = 0;
    //    for (int i = 1; i < (int)faces.size(); ++i) {
    //        HalfEdge*j = faces[i]->eHead;
    //        do {
    //            to[edgeCnt] = j->twin->parFace->id;
    //            nxt[edgeCnt] = head[i];
    //            printf("%d\n", j->twin->parFace->id);
    //            //printf("%d -> %d\n", i, to[edgeCnt]);
    //            head[i] = edgeCnt++;
    //            j = j->succ;
    //        } while (j != faces[i]->eHead);
    //    }
    }
    int findLocation(Point a) {
        Point p, v;
        for (int i = 1; i < (int)faces.size(); ++i) {
            bool ok = true;
            HalfEdge*j = faces[i]->eHead;
            do {
                p = pointPool[j->orig];
                v = pointPool[j->succ->orig];
                if ((v - p).cross(a - p) < 0) {
                    ok = false;
                    break;
                }
                j = j->succ;
            } while (j != faces[i]->eHead);
            if (ok) {
                return faces[i]->id;
            }
        }
        return 0;
    }
    void print() {
        printf("sitePoints = new Array(");
        printf("{x: %lf, y: %lf}", sites[0].x, sites[0].y);
        for (int i = 1; i < (int)sites.size(); ++i) {
            printf(", {x: %lf, y: %lf}", sites[i].x, sites[i].y);
        }
        puts(");");
        printf("faces = new Array(\n");
        for (int i = 1; i < (int)faces.size(); ++i) {
            HalfEdge*j = faces[i]->eHead;
            printf("new Array({x: %lf, y: %lf}", pointPool[j->orig].x, pointPool[j->orig].y);
            j = j->succ;
            while (j != faces[i]->eHead) {
                printf(", {x: %lf, y: %lf}", pointPool[j->orig].x, pointPool[j->orig].y);
                j = j->succ;
            }
            if (i != (int)faces.size() - 1)
                puts("),");
            else puts(")");
        }
        puts(");");
    }
    void solve(Point a) {
        buildDualGraph();
        int x = findLocation(a);
    //    printf("%d\n", x);
        int *que = new int[10000], *dis = new int[1000];
        int ql, qr;
        memset(dis, 0x3f, (sizeof (int)) * 1000);
        dis[x] = 0;
        que[ql = qr = 0] = x;
        while (ql <= qr) {
            int u = que[ql++];
            for (int i = head[u]; i != -1; i = nxt[i]) {
                int v = to[i];
                if (dis[u] + 1 <= dis[v]) {
                    dis[v] = dis[u] + 1;
                    que[++qr] = v;
                }
            }
        }
        printf("%d\n", dis[0]);
    }
};

int main() {
#ifdef lol
    freopen("3199.in", "r", stdin);
    freopen("3199.out", "w", stdout);
#endif

    int t; scanf("%d", &t);
    while (t--) {
        int n; scanf("%d", &n);
        Point a, b;
        cin >> a.x >> a.y;
        cin >> b.x >> b.y;
        Point *p = new Point[n];
        for (int i = 0; i < n; ++i)
            cin >> p[i].x >> p[i].y;
        VoronoiN2 *v = new VoronoiN2(p, n, Point(0, 0), a);
        //v->print();
        v->solve(b);
    }

    return 0;
}
时间: 2024-09-28 21:13:21

BZOJ 3199: [Sdoi2013]escape的相关文章

bzoj 3198: [Sdoi2013]spring 题解

[原题] 3198: [Sdoi2013]spring Time Limit: 40 Sec  Memory Limit: 256 MB Submit: 253  Solved: 95 Description Input Output Sample Input 3 3 1 2 3 4 5 6 1 2 3 0 0 0 0 0 0 4 5 6 Sample Output 2 HINT [题解]这道题明明是水题,坑了我两天!!!真是伤心.发现哈希都不熟练了. 首先很容易想到是2^6枚举01状态,使得1

BZOJ 3199 escape

题目链接:http://www.lydsy.com:808/JudgeOnline/problem.php?id=3199 题意:给出n个点.对于平面上任意一点p,p到n个点中的哪个点的距离最近我们就称p就被哪个点控制.有可能p被多个点同时控制(距离相等的时候).给出一个矩形框(0,0,X,Y)以及框内一点q.从q到达矩形框上的途中,最少被多少个点控制过? 思路:n个点中的每个点有一个控制区域.某个点和其他点中垂线组成的半平面交就是这个区域.这个可以用凸包计算.这里是一个nlogn求凸包的算法,

[BZOJ 3129] [Sdoi2013] 方程 【容斥+组合数取模+中国剩余定理】

题目链接:BZOJ - 3129 题目分析 使用隔板法的思想,如果没有任何限制条件,那么方案数就是 C(m - 1, n - 1). 如果有一个限制条件是 xi >= Ai ,那么我们就可以将 m 减去 Ai - 1 ,相当于将这一部分固定分给 xi,就转化为无限制的情况了. 如果有一些限制条件是 xi <= Ai 呢?直接来求就不行了,但是注意到这样的限制不超过 8 个,我们可以使用容斥原理来求. 考虑容斥:考虑哪些限制条件被违反了,也就是说,有哪些限制为 xi <= Ai 却是 xi

BZOJ 3129 [Sdoi2013]方程 不定方程解的个数+组合数取模

题意:链接 方法:不定方程解的个数+组合数取模 解析: 先看n1与n2的部分的限制. 对于后半部分的限制来说,我们直接减去An1+i?1就可以转化一下求正整数解. 但是前半部分呢? 跟上一道猴子那个很像. 所以我们容斥搞就行了. 但是这道题好像不好写的地方不在这? 这题TMD不就是礼物吗! 大组合数取模如何取? 请参见我<BZOJ 礼物>的题解. 另外吐槽题干 明明是X1+X2+-+Xn=m 并不是小于等于 代码: #include <cstdio> #include <cs

[BZOJ 3198] [Sdoi2013] spring 【容斥 + Hash】

题目链接:BZOJ - 3198 题目分析 题目要求求出有多少对泉有恰好 k 个值相等. 我们用容斥来做. 枚举 2^6 种状态,某一位是 1 表示这一位相同,那么假设 1 的个数为 x . 答案就是 sigma((-1)^(x - k) * AnsNow * C(x, k)) .注意 x 要大于等于 k. 对于一种状态,比如 10110,就是要保证第 1, 3, 4 个值相同. 这些值相同的对数怎么来求呢?使用Hash. 将这些位上的值 Hash 成一个数,然后枚举  [1, i] , 每次求

BZOJ 3130: [Sdoi2013]费用流 网络流+二分

3130: [Sdoi2013]费用流 Time Limit: 10 Sec  Memory Limit: 128 MBSec  Special JudgeSubmit: 1230  Solved: 598[Submit][Status][Discuss] Description Alice和Bob在图论课程上学习了最大流和最小费用最大流的相关知识.     最大流问题:给定一张有向图表示运输网络,一个源点S和一个汇点T,每条边都有最大流量.一个合法的网络流方案必须满足:(1)每条边的实际流量都

Bzoj 3124: [Sdoi2013]直径 题解

3124: [Sdoi2013]直径 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 1222  Solved: 580[Submit][Status][Discuss] Description 小Q最近学习了一些图论知识.根据课本,有如下定义.树:无回路且连通的无向图,每条边都有正整数的权值来表示其长度.如果一棵树有N个节点,可以证明其有且仅有N-1 条边. 路径:一棵树上,任意两个节点之间最多有一条简单路径.我们用 dis(a,b)表示点a和点

Bzoj 3131 [Sdoi2013]淘金 题解

3131: [Sdoi2013]淘金 Time Limit: 30 Sec  Memory Limit: 256 MBSubmit: 733  Solved: 363[Submit][Status][Discuss] Description 小Z在玩一个叫做<淘金者>的游戏.游戏的世界是一个二维坐标.X轴.Y轴坐标范围均为1..N.初始的时候,所有的整数坐标点上均有一块金子,共N*N块.    一阵风吹过,金子的位置发生了一些变化.细心的小Z发现,初始在(i,j)坐标处的金子会变到(f(i),

bzoj 3123: [Sdoi2013]森林

如果题号没记错的话,2588是一个树上的主席树查询.这个题就是多了个合并而已.每一次都把小的合并到大的上就好了(所谓启发式2333) (主席树真是个好东西2333) (上部分为本蒟蒻不知道为什么RE到死都RE的代码,,,挖坑) (个人感觉主席树这种东西里离散不离散没什么区别的(常数而已),毕竟是log的,大个几次方就是多了个常数而已) 1 /*#include<bits/stdc++.h> 2 #define N 100005 3 #define M 40000005 4 #define LL