• Gym 103081 部分题解


    C. Safe Distance

    解题报告

    转化一下:一个动点要与一个点保持一定距离,临界就是以这个点画圆。

    所以可以二分一个距离,然后以这个距离为半径,把所有点对应的圆画出来

    ……当然不是要做计算几何,可以发现这样能到达的充要条件是,这些圆连同边界没有把原点框住

    所以就把相交的圆、圆和相交的边界都加进并查集里,如果上下、左右、左下、右上边界有一对是在同一集合里的,就说明原点被这个集合里的圆、边界框住了,也就是这个距离不合法

    代码实现

    namespace CG {
        const double eps = 1e-6;
    
        struct Vector {
            double x, y;
            Vector(double _x = 0, double _y = 0) : x(_x), y(_y) {}
        }; typedef Vector Point;
    
        const Vector ZERO = Vector(0, 0);
    
        struct Line {
            double a, b, c;
            Line(double _a = 0, double _b = 0, double _c = 0) : a(_a), b(_b), c(_c) {}
        };
    
        struct Circle {
            Point C; double r;
            Circle(Point _C = ZERO, double _r = 0) : C(_C), r(_r) {}
        };
    
        double sqr(double x) { return x * x; }
        double dist_sqr(Point a, Point b) {
            return sqr(a.x - b.x) + sqr(a.y - b.y);
        }
        double dist_sqr(Point a, Line l) {
            return sqr(l.a * a.x + l.b * a.y + l.c) / (sqr(l.a) + sqr(l.b));
        }
        bool isCircIntersect(Circle c1, Circle c2) {
            return dist_sqr(c1.C, c2.C) - sqr(c1.r + c2.r) < eps;
        }
        bool isCircIntersect(Circle c1, Line l) {
            return dist_sqr(c1.C, l) < sqr(c1.r);
        }
    } using namespace CG;
    
    const int MAXN = 1000 + 10;
    
    Point xy;
    int n;
    Circle cc[MAXN];
    
    int uu, dd, ll, rr; // border
    Line u0, d0, l0, r0;
    
    struct DSU {
        int u[MAXN];
        
        void clear() { for (int i = 1; i <= n + 4; ++i) u[i] = 0; }
        int Find(int x) { return !u[x] ? x : u[x] = Find(u[x]); }
        void Merge(int x, int y) {
            x = Find(x); y = Find(y);
            if (x != y) u[x] = y;
        }
    } dsu;
    
    bool check(double len) {
        dsu.clear();
        for (int i = 1; i <= n; ++i) {
            cc[i].r = len;
            if (isCircIntersect(cc[i], u0)) dsu.Merge(i, uu);
            if (isCircIntersect(cc[i], d0)) dsu.Merge(i, dd);
            if (isCircIntersect(cc[i], l0)) dsu.Merge(i, ll);
            if (isCircIntersect(cc[i], r0)) dsu.Merge(i, rr);
        }
        for (int i = 1; i <= n; ++i) {
            for (int j = i + 1; j <= n; ++j) {
                if (isCircIntersect(cc[i], cc[j])) dsu.Merge(i, j);
            }
        }
        return !(dsu.Find(uu) == dsu.Find(dd) 
        || dsu.Find(ll) == dsu.Find(rr) 
        || dsu.Find(uu) == dsu.Find(rr)
        || dsu.Find(ll) == dsu.Find(dd));
    }
    
    int main() {
        std::ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
        cin >> xy.x >> xy.y;
        cin >> n;
        uu = n + 1; dd = n + 2; ll = n + 3; rr = n + 4;
        u0 = Line(0, 1, -xy.y); d0 = Line(0, 1, 0);
        l0 = Line(1, 0, 0); r0 = Line(1, 0, -xy.x);
        for (int i = 1; i <= n; ++i) {
            Point C0; cin >> C0.x >> C0.y;
            cc[i] = Circle(C0, 0);
        }
        double l = eps, r = 1.0e7, mid = 0;
        for (int i = 0; i < 100; ++i) {
            mid = (l + r) / 2.0;
            if (check(mid)) l = mid;
            else r = mid;
        } cout << mid << endl;
        return 0;
    }
    

    D. Jogging

    解题报告

    首先可以发现一个性质:最好的情况当然是每天只新增一条边

    所以可以枚举当天新增的边,然后判断跑到那里能不能满足最大值的限制
    因为可以在路上反复横跳所以最小值的限制就不用管了

    所以只需要一个最短路就可以了

    代码实现

    const int MAXN = 2000000 + 10;
    
    int n, m, minl, maxl;
    
    struct Edge {
        int v, w; Edge(int _v = 0, int _w = 0) : v(_v), w(_w) {}
        bool operator < (const Edge &th) const { return w > th.w; }
    } redges[MAXN]; typedef Edge Node;
    std::vector<Edge> G[MAXN];
    
    int dist[MAXN];
    
    void sp() {
        static bool vis[MAXN];
        memset(dist, 0x3f, sizeof dist);
        std::priority_queue<Node> q;
        q.push({1, dist[1] = 0});
        while (!q.empty()) {
            int u = q.top().v; q.pop();
            if (vis[u]) continue;
            vis[u] = true;
            forall (G[u], i) {
                int v = G[u][i].v, w = G[u][i].w;
                if (dist[v] > dist[u] + w) 
                    q.push({v, dist[v] = dist[u] + w});
            }
        }
    }
    
    int main() {
        n = read(); m = read(); minl = read(); maxl = read();
        for (int i = 1; i <= m; ++i) {
            int u = read() + 1; int v = read() + 1; int w = read();
            redges[i] = {u, v}; 
            G[u].push_back({v, w});
            G[v].push_back({u, w});
        } sp();
        int ans = 0;
        for (int i = 1; i <= m; ++i) {
            int u = redges[i].v, v = redges[i].w;
            int dis = 2 * std::min(dist[u], dist[v]);
            if (dis < maxl) ++ans;
        } printf("%d
    ", ans);
        return 0;
    }
    
  • 相关阅读:
    jvm 学习
    架构师
    关于javaScript堆、栈和队列
    ES6-对象的扩展-属性名表达式
    JS 中 ++i 和i++的区别
    递归算法讲解
    Ztree 仿淘宝树结构完美实现 移动 右键增删改
    jquery zTree异步加载实例
    【zTree】简单实例与异步加载实例
    win10中用命令行打开服务
  • 原文地址:https://www.cnblogs.com/handwer/p/15128706.html
Copyright © 2020-2023  润新知