调了1+day?
卡精度?
5-10-60-90-95-100?
或称最慢提交?
心累
#pragma GCC optimize(3)
#include "bits/stdc++.h"
//#define SHOW_CONSOLE
//#include <graphics.h>
using namespace std;
namespace io
{
#define USE_FEAD_IO
#ifdef USE_FEAD_IO
#define BUF_SIZE 5000010
char buf[BUF_SIZE]; int cur = BUF_SIZE; FILE * in = stdin;
inline char gnc()
{
if (cur == BUF_SIZE) { fread(buf, BUF_SIZE, 1, in); cur = 0; }
return buf[cur++];
}
#else
inline char gnc()
{
return (char)getchar();
}
#endif
template<typename T>
inline void gi(T &dx)
{
= 0;
dx int yc = gnc();
bool nega = false;
while (yc < '0' || yc > '9') { nega = (yc == '-' ? true : nega); yc = gnc(); }
while (yc >= '0' && yc <= '9') { dx = (T)(dx * 10 + yc - '0'); yc = gnc(); }
if (nega) { dx = -dx; }
}
void gc(char &a)
{
do a = gnc(); while (!isgraph(a));
}
void gpc(char &a)
{
do a = gnc(); while (!isprint(a));
}
void gss(char * jc)
{
*jc = gnc();
while (!isgraph(*jc)) * jc = gnc();
while (isgraph(*jc)) * ++jc = gnc();
*jc = 0;
}
#if __cplusplus >= 201103l || (defined(_MSVC_LANG) && _MSVC_LANG >= 201103l)
template<typename T, typename ...Args> inline void gi(T &a, Args &...args)
{ gi(a); gi(args...); }
#else
template<typename t1, typename t2> inline void gi(t1 &a, t2 &b) { gi(a); gi(b); }
template<typename t1, typename t2, typename t3> inline void gi(t1 &a, t2 &b, t3 &c) { gi(a); gi(b); gi(c); }
template<typename t1, typename t2, typename t3, typename t4> inline void gi(t1 &a, t2 &b, t3 &c, t4 &d) { gi(a); gi(b); gi(c); gi(d); }
template<typename t1, typename t2, typename t3, typename t4, typename t5> inline void gi(t1 &a, t2 &b, t3 &c, t4 &d, t5 &e) { gi(a); gi(b); gi(c); gi(d); gi(e); }
#endif
}
using namespace io;
const double eps = 1e-3;
const double pi = acos(-1);
inline double fuck(double x)
{
while (x < 0)x += pi;
while (x >= pi)x -= pi;
return x;
}
struct Point
{
double x, y;
() :x(0), y(0) {}
Point(double x, double y) :x(x), y(y) {}
Point};
typedef Point Vector;
inline Vector operator +(const Vector &A, const Vector &B) { return Vector(A.x + B.x, A.y + B.y); }
inline Vector operator -(const Point &A, const Point &B) { return Vector(A.x - B.x, A.y - B.y); }
inline Vector operator *(const Vector &A, double p) { return Vector(A.x*p, A.y*p); }
inline Vector operator /(const Vector &A, double p) { return Vector(A.x / p, A.y / p); }
inline ostream &operator<<(ostream&a, const Vector &p)
{
return a << "(" << p.x << "," << p.y << ")";
}
// 考虑eps
inline int sgn(double x)
{
if (fabs(x) < eps) return 0; else return x < 0 ? -1 : 1;
}
inline bool operator<(const Point&a, const Point&b)
{
return sgn(a.x - b.x) < 0 || (sgn(a.x - b.x) == 0 && sgn(a.y - b.y) < 0);
}
inline bool operator==(const Point&a, const Point&b)
{
return sgn(a.x - b.x) == 0 && sgn(a.y - b.y) == 0;
}
//向量极角
inline double angle(const Vector &v) { return atan2(v.y, v.x); }
//点积
inline double dot(const Vector & A, const Vector & B) { return A.x*B.x + A.y*B.y; }
inline double length(const Vector & A) { return sqrt(dot(A, A)); }
inline double angle(const Vector & A, const Vector & B) { return acos(dot(A, B) / length(A) / length(B)); }
//叉积
inline double cross(const Vector & A, const Vector & B) { return A.x*B.y - A.y*B.x; }
inline double area2(const Point &A, const Point & B, const Point & C) { return cross(B - A, C - A); }
//向量旋转
inline Vector rotate(const Vector & A, double rad)
{
return Vector(A.x*cos(rad) - A.y*sin(rad), A.x*sin(rad) + A.y*cos(rad));
}
//计算向量的单位法线,左转90度以后把长度归一化,调用前确保A不是零向量
inline Vector norm(const Vector & A)
{
double L = length(A);
return Vector(-A.y / L, A.x / L);
}
//直线交点
inline Point getIntersection(const Point & P, const Vector & v, const Point & Q, const Vector & w)
{
= P - Q;
Vector u double t = cross(w, u) / cross(v, w);
return P + v*t;
}
//点到直线的距离
inline double distanceToLine(const Point & P, const Point & A, const Point & B)
{
= B - A, v2 = P - A;
Vector v1 return fabs(cross(v1, v2)) / length(v1);
}
//点到线段的距离
inline double distanceToSegment(const Point & P, const Point & A, const Point & B)
{
if (A == B) return length(P - A);
= B - A, v2 = P - A, v3 = P - B;
Vector v1 if (sgn(dot(v1, v2)) < 0) return length(v2);
else if (sgn(dot(v1, v3)) < 0) return length(v3);
else return fabs(cross(v1, v2)) / length(v1);
}
//线段相交判定(不允许端点处相交)
inline bool segmentIntersectionp(const Point & a1, const Point & a2, const Point & b1, const Point & b2)
{
double c1 = cross(a2 - a1, b1 - a1), c2 = cross(a2 - a1, b2 - a1),
= cross(b2 - b1, a1 - b1), c4 = cross(b2 - b1, a2 - b1);
c3 return sgn(c1)*sgn(c2) < 0 && sgn(c3)*sgn(c4) < 0;
}
//线段相交判定(允许端点处相交)
inline bool on(const Point & p, const Point & a1, const Point & a2)
{
return sgn(cross(a1 - p, a2 - p)) == 0 && sgn(dot(a1 - p, a2 - p)) < 0;
}
//线段相交判定(允许端点处相交)
bool segmentIntersection(const Point & a1, const Point & a2, const Point & b1, const Point & b2)
{
double c1 = cross(a2 - a1, b1 - a1), c2 = cross(a2 - a1, b2 - a1),
= cross(b2 - b1, a1 - b1), c4 = cross(b2 - b1, a2 - b1);
c3 if (sgn(c1)*sgn(c2) < 0 && sgn(c3)*sgn(c4) < 0) return true;
if (sgn(c1) == 0 && on(b1, a1, a2)) return true;
if (sgn(c2) == 0 && on(b2, a1, a2)) return true;
if (sgn(c3) == 0 && on(a1, b1, b2)) return true;
if (sgn(c4) == 0 && on(a2, b1, b2)) return true;
return false;
}
//多边形的有向面积
double PolygonArea(Point *p, int n)
{
double area = 0;
for (int i = 1; i < n; i++) {
+= cross(p[i] - p[0], p[i + 1] - p[0]);
area }
return area / 2;
}
struct Circle
{
;
Point cdouble r;
() {}
Circle(const Point & c, double r) :c(c), r(r) {}
Circle(double a) const
Point point{
return Point(c.x + r*cos(a), c.y + r*sin(a));
}
};
struct Line
{
;
Point p;
Vector v(const Point & p, const Vector & v) :p(p), v(v) {}
Line(double t) const
Point point{
return p + v*t;
}
(double d) const
Line move{//平移
return Line(p + norm(v)*d, v);
}
};
inline bool on(const Point & p, const Line &a)
{
return on(p, a.p, a.p + a.v);
}
inline bool on(const Point & p, const Circle &c)
{
return !(sgn(length(p - c.c) - c.r));
}
inline double length(const Line & a)
{
return length(a.v);
}
//弧度转角度
inline double lineAngleDegree(const Vector &v)
{//0<=x<180
double ang = angle(v) * 180.0 / pi;
while (sgn(ang)<0) ang += 360.0;
while (sgn(ang - 180) >= 0) ang -= 180.0;
return ang;
}
//构造直线
inline Line getLine(const Point & p1, const Point & p2)
{
return Line(p1, p2 - p1);
}
//构造直线
inline Line getLine(double x1, double y1, double x2, double y2)
{
return getLine(Point(x1, y1), Point(x2, y2));
}
//求直线交点
inline Point getIntersection(const Line & a, const Line & b)
{
return getIntersection(a.p, a.v, b.p, b.v);
}
//直线与圆的交点
int getIntersection(const Line &L, const Circle & C, double &t1, double &t2, vector<Point>& sol)
{
double a = L.v.x, b = L.p.x - C.c.x, c = L.v.y, d = L.p.y - C.c.y;
double e = a*a + c*c, f = 2 * (a*b + c*d), cir = b*b + d*d - C.r*C.r;
double delta = f*f - 4 * e*cir;
if (sgn(delta)<0) return 0;
if (sgn(delta) == 0) {
= t2 = -f / (2 * e); sol.push_back(L.point(t1));
t1 return 1;
}
= (-f - sqrt(delta)) / (2 * e); sol.push_back(L.point(t1));
t1 = (-f + sqrt(delta)) / (2 * e); sol.push_back(L.point(t2));
t2 return 2;
}
//直线与圆求交
int getIntersection(const Line &L, const Circle & C)
{
double a = L.v.x, b = L.p.x - C.c.x, c = L.v.y, d = L.p.y - C.c.y;
double e = a*a + c*c, f = 2 * (a*b + c*d), cir = b*b + d*d - C.r*C.r;
double delta = f*f - 4 * e*cir;
if (sgn(delta)<0) return 0;
double t1, t2;
if (sgn(delta) == 0) {
= t2 = -f / (2 * e);
t1 if (t1 < 0 || t1 > 1) return 0;
return 1;
}
int ans = 2;
= (-f - sqrt(delta)) / (2 * e);
t1 = (-f + sqrt(delta)) / (2 * e);
t2 if (t1 < 0 || t1 > 1) --ans;
if (t2 < 0 || t2 > 1) --ans;
return ans;
}
//两圆相交的代码
int getIntersection(const Circle & C1, const Circle & C2, vector<Point>& sol)
{
double d = length(C1.c - C2.c);
if (sgn(d) == 0) {
if (sgn(C1.r - C2.r) == 0) return -1;//两圆重合
return 0;
}
if (sgn(C1.r + C2.r - d) < 0) return 0;
if (sgn(fabs(C1.r - C2.r) - d) > 0) return 0;
double a = angle(C2.c - C1.c);
double da = acos((C1.r*C1.r + d*d - C2.r*C2.r) / (2 * C1.r*d));
= C1.point(a - da), p2 = C1.point(a + da);
Point p1
.push_back(p1);
solif (p1 == p2) return 1;
.push_back(p2);
solreturn 2;
}
//过点p到圆C的切线。v[i]是第i条切线的向量。返回切线条数
int getTangents(const Point & p, const Circle &C, vector<Vector>& v)
{
= C.c - p;
Vector u double dist = length(u);
if (dist < C.r) return 0;
else if (sgn(dist - C.r) == 0) {
.push_back(rotate(u, pi / 2));
vreturn 1;
} else {
double ang = asin(C.r / dist);
.push_back(rotate(u, -ang));
v.push_back(rotate(u, +ang));
vreturn 2;
}
}
// 两圆的公切线。
// 函数返回切线的条数。-1表示无穷条切线。
// a[i]和b[i]分别是第i条切线在圆A和圆B上的切点
int getTangents(Circle A, Circle B, Point* a, Point* b)
{
int cnt = 0;
if (A.r<B.r) {
(A, B); swap(a, b);
swap}
double d2 = (A.c.x - B.c.x)*(A.c.x - B.c.x) + (A.c.y - B.c.y)*(A.c.y - B.c.y);
double rdiff = A.r - B.r;
double rsum = A.r + B.r;
if (d2 < rdiff * rdiff) return 0;//内含
double base = atan2(B.c.y - A.c.y, B.c.x - A.c.x);
if (d2 == 0 && A.r == B.r) return -1;//无限多条切线
if (d2 == rdiff * rdiff) {//内切,1条切线
[cnt] = A.point(base); b[cnt] = B.point(base); cnt++;
areturn 1;
}
//有外共切线
double ang = acos((A.r - B.r) / sqrt(d2));
[cnt] = A.point(base + ang); b[cnt] = B.point(base + ang); cnt++;
a[cnt] = A.point(base - ang); b[cnt] = B.point(base - ang); cnt++;
aif (d2 == rsum*rsum) {//一条内公切线
[cnt] = A.point(base); b[cnt] = B.point(pi + base); cnt++;
a} else if (d2>rsum*rsum) {//两条内公切线
= acos(rsum / sqrt(d2));
ang [cnt] = A.point(base + ang); b[cnt] = B.point(pi + base + ang); cnt++;
a[cnt] = A.point(base - ang); b[cnt] = B.point(pi + base - ang); cnt++;
a}
return cnt;
}
//求三角形外接圆
(const Point & p1, const Point & p2, const Point & p3)
Circle circumscribedCircle{
double Bx = p2.x - p1.x, By = p2.y - p1.y;
double Cx = p3.x - p1.x, Cy = p3.y - p1.y;
double D = 2 * (Bx*Cy - By*Cx);
double cx = (Cy*(Bx*Bx + By*By) - By*(Cx*Cx + Cy*Cy)) / D + p1.x;
double cy = (Bx*(Cx*Cx + Cy*Cy) - Cx*(Bx*Bx + By*By)) / D + p1.y;
= Point(cx, cy);
Point p return Circle(p, length(p1 - p));
}
//求三角形内接圆
(const Point & p1, const Point & p2, const Point & p3)
Circle inscribedCircle{
double a = length(p2 - p3);
double b = length(p3 - p1);
double c = length(p1 - p2);
= (p1*a + p2*b + p3*c) / (a + b + c);
Point p return Circle(p, distanceToLine(p, p1, p2));
}
const int maxn = 666;
extern Point f[];
struct graph
{
static const int vv = maxn * maxn * 4;
static const int ee = vv * 6;
int beg[vv], nxt[ee], to[ee];
double len[ee];
int e;
void aa(int x, int y, double z)
{
++e; nxt[e] = beg[x]; to[e] = y; len[e] = z; beg[x] = e;
}
void ae(int x, int y, double z)
{
if (x == y) return;
(x, y, z); aa(y, x, z);
aa}
} g;
[graph::vv];
Point fint n;
[maxn];
Circle cir[maxn], zzb[maxn];
Point zza, t;
Point sint cc;
<Point, int> tra;
map<Vector> zv;
vectorint tr(const Point & x)
{
if (tra[x]) return tra[x];
++cc;
[cc] = x;
freturn tra[x] = cc;
}
bool ok(const Line &a)
{
for (int i = 1; i <= n; ++i) {
if (getIntersection(a, cir[i]) == 2) {
return false;
}
}
return true;
}
<int> v;
vector<double> cac;
vector<int> sb;
vectorbool sbcmp(int x, int y)
{
return cac[x] < cac[y];
}
double dis[graph::vv];
bool vis[graph::vv];
struct info
{
int x;
double d;
bool operator<(const info &a) const
{
return d > a.d;
}
};
void spfa(int s, int t)
{
(dis, dis + graph::vv, 1e20);
fill[s] = 0;
dis//memset(vis, false, sizeof vis);
<info> q;
priority_queue.push(info({ s, 0 }));
qwhile (!q.empty()) {
int x = q.top().x; q.pop();
if (x == t) return;
if (vis[x]) continue;
[x] = true;
visfor (int i = g.beg[x]; i; i = g.nxt[i]) {
int y = g.to[i];
double w = g.len[i];
if (dis[y] > dis[x] + w) {
[y] = dis[x] + w;
dis.push(info({ y,dis[y] }));
q}
}
}
return;
}
int main()
{
(s.x, s.y, t.x, t.y);
gi(n);
gifor (int i = 1; i <= n; ++i) {
(cir[i].c.x, cir[i].c.y, cir[i].r);
gi}
int sss = tr(s), ttt = tr(t);
{
if (ok(getLine(s, t))) {
.ae(sss, ttt, length(t - s));
g}
}
for (int i = 1; i <= n; ++i) {
(s, cir[i], zv);
getTangentsfor (int j = 0; j < zv.size(); ++j) {
(s, zv[j]);
Line ldouble f1, f2;
<Point> sol;
vector/*assert*/(getIntersection(l, cir[i], f1, f2, sol) == 1);
if (ok(Line(s, zv[j] * f1))) {
= s + zv[j] * f1;
Point ss .ae(tr(s), tr(ss), length(zv[j] * f1));
g}
}
.clear();
zv(t, cir[i], zv);
getTangentsfor (int j = 0; j < zv.size(); ++j) {
(t, zv[j]);
Line ldouble f1, f2;
<Point> sol;
vector/*assert*/(getIntersection(l, cir[i], f1, f2, sol) == 1);
if (ok(Line(t, zv[j] * f1))) {
= t + zv[j] * f1;
Point ss .ae(tr(t), tr(ss), length(zv[j] * f1));
g}
}
.clear();
zv}
for (int i = 1; i <= n; ++i) {
for (int j = i + 1; j <= n; ++j) {
int x = getTangents(cir[i], cir[j], zza, zzb);
// assert x = 2 or 3 or 4
/*assert*/(x >= 2);
for (int k = 0; k < x; ++k) {
int aa = tr(zza[k]), bb = tr(zzb[k]);
= getLine(zza[k], zzb[k]);
Line t if (!ok(t)) continue;
.ae(aa, bb, length(t));
g}
}
}
for (int k = 1; k <= n; ++k) {
.clear(); cac.clear(); sb.clear();
vfor (int i = 1; i <= cc; ++i) {
if (on(f[i], cir[k])) {
.push_back(i);
v.push_back(angle(f[i] - cir[k].c));
cac}
}
for (int i = 0; i < v.size(); ++i) sb.push_back(i);
(sb.begin(), sb.end(), sbcmp);
sortfor (int ii = 1; ii < v.size(); ++ii) {
int x = sb[ii - 1], y = sb[ii];
.ae(tr(f[v[x]]), tr(f[v[y]]), cir[k].r * fuck(cac[y] - cac[x]));
g}
{
int x = sb[v.size() - 1], y = sb[0];
.ae(tr(f[v[x]]), tr(f[v[y]]), cir[k].r * fuck(cac[y] - cac[x]));
g}
}
(sss, ttt);
spfa("%.1lf\n", dis[ttt]);
printfreturn 0;
}