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;
}