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\n", ans);
    return 0;
}
posted @ 2021-08-11 16:03  Handwer  阅读(161)  评论(0编辑  收藏  举报