来水一篇题解
显然路径 A 1 → A → B → A 6 A_1\to A\to B\to A_6 A1→A→B→A6 最短,可以想到最优路径就是沿着“最内侧”的边走,我们可以使用三角形两边之和大于第三边进行浅浅的证明我就不证了(逃。
而我们可以使用半平面交求解“最内侧”多边形,所以很容易想到链接 A 1 A 6 A_1A_6 A1A6 ,这样就形成了一个封闭图形。但是 O ( n 2 ) O(n^2) O(n2) 条边略微有辣么亿点点大,所以我们又发现了对于每个点,“最内侧”的边,一定是与其相连,另一端编号最大的边,所以当加边的时候可以先排一下序。答案就是交平面的周长减去 A 1 A n A_1A_n A1An 。
贴上代码
namespace geometry {
double pi = acos(-1);
struct point {
double x, y;
point(double _x = 0, double _y = 0)
: x(_x), y(_y) {}
inline void read() {
cin >> x >> y;
}
inline friend double dist(point a, point b) {
double sq1 = (a.x - b.x), sq2(a.y - b.y);
return sqrt(sqr(sq1) + sqr(sq2));
}
inline friend bool operator==(point a, point b) {
return a.x == b.x && a.y == b.y;
}
inline friend bool operator!=(point a, point b) {
return a.x != b.x || a.y != b.y;
}
inline friend point operator-(point a, point b) {
return point(a.x - b.x, a.y - b.y);
}
inline friend point operator+(point a, point b) {
return point(a.x + b.x, a.y + b.y);
}
inline friend double cross(point a, point b) {
return a.x * b.y - a.y * b.x;
}
inline friend double dot(point a, point b) {
return a.x * b.x + a.y * b.y;
}
inline friend point operator*(point a, double b) {
return point(a.x * b, a.y * b);
}
};
using line = pair<point, point>;
double dist_sqr(point a, point b) {
return sqr(a.x - b.x) + sqr(a.y - b.y);
}
inline double len(point p, point l, point r) {
point x1 = l - p, x2 = r - p;
return cross(x1, x2) / dist(l, r);
}
double c(double x) {
if (x > 1)
return 1;
if (x < -1)
return -1;
return x;
}
inline double angle(point a, point b, point rt = point()) {
return acos(c(dot(a - rt, rt - b) / dist(a, rt) / dist(b, rt)));
}
inline double check(point a1, point a2, point b1, point b2) {
return (a2.x - a1.x) * (b2.y - b1.y) - (b2.x - b1.x) * (a2.y - a1.y);
}
vector<point> convex_hull(vector<point> p) {
point g = p[0];
for (int i = 1; i < p.size(); i++) {
if (p[i].y < g.y || (p[i].y == g.y && p[i].x < g.x))
g = p[i];
}
sort(p.begin(), p.end(), [&](point a, point b)->bool {
double sum = cross(a - g, b - g);
if (sum > 0)
return true;
if (sum < 0)
return false;
return dist(a, g) < dist(b, g);
});
vector<point> res(p.size());
int cnt = -1;
res[++cnt] = p[0];
for (int i = 1; i < p.size(); i++) {
while (cnt > 0 && check(res[cnt - 1], res[cnt], res[cnt], p[i]) <= 0)
cnt--;
res[++cnt] = p[i];
}
res.resize(cnt + 1);
return res;
}
double diameter(vector<point> p) {
int sz = p.size();
if (sz < 3)
return dist_sqr(p[0], p[(1) % sz]);
int j = 2;
double res = 0;
p.push_back(p[0]);
for (int i = 0; i < sz; i++) {
while (cross(p[i + 1] - p[i], p[(j + 1) % sz] - p[i]) > cross(p[i + 1] - p[i], p[j] - p[i]))
j = (j + 1) % sz;
res = max(res, dist_sqr(p[i], p[j]));
}
return res;
}
point itsect(line a, line b) {
return a.first + a.second * (cross(b.second, a.first - b.first) / cross(a.second, b.second));
}
bool right(line a, point b) {
return cross(a.second, b - a.first) < EPS;
}
vector<point> plane(vector<line> v) {
sort(v.begin(), v.end(), [&](line a, line b)->bool {
return atan2(a.second.y, a.second.x) < atan2(b.second.y, b.second.x);
});
int l = 0, r = 0;
vector<point> t(v.size());
vector<line> q(v.size());
q[0] = v[0];
for (int i = 1; i < v.size(); i++) {
while (l < r && right(v[i], t[r]))
r--;
while (l < r && right(v[i], t[l + 1]))
l++;
q[++r] = v[i];
if (abs(cross(q[r].second, q[r - 1].second)) < EPS) {
if (right(q[r], q[r - 1].first) && dot(q[r].second, q[r - 1].second) < EPS)
return vector<point>();
r--;
if (!right(q[r], v[i].first))
q[r] = v[i];
}
if (l < r)
t[r] = itsect(q[r], q[r - 1]);
}
while (l < r && right(q[l], t[r]))
r--;
if (r - l <= 1)
return vector<point>();
t[l] = itsect(q[l], q[r]);
return vector<point>(t.begin() + l, t.begin() + r + 1);
}
double area(vector<point> p) {
if (p.size() <= 2)
return 0;
double res = 0;
for (int i = 0; i < p.size() - 1; i++)
res += cross(p[i], p[i + 1]);
res += cross(p.back(), p[0]);
return res / 2;
}
double circumference(vector<point> p) {
if (p.size() < 2)
return 0;
if (p.size() == 2)
return dist(p[0], p[1]);
double res = 0;
for (int i = 0; i < p.size() - 1; i++)
res += dist(p[i], p[i + 1]);
res += dist(p.back(), p[0]);
return res;
}
double area(point p1, point p2, point p3) {
return abs(cross(p1 - p3, p2 - p3)) / 2;
}
}
using namespace geometry;
signed main() {
ios::sync_with_stdio(false);
cin.tie(0);
int n, m; cin >> n >> m;
vector<point> p(n);
for (int i = 0; i < n; i++)
p[i].read();
vector<line> l;
vector<vector<int>> dn(n);
for (int i = 0; i < m; i++) {
int x, y; cin >> x >> y;
--x, --y;
if (x < y)
swap(x, y);
dn[x].push_back(y);
}
for (int i = 0; i < n; i++)
sort(dn[i].begin(), dn[i].end());
for (int i = 0; i < n; i++) {
int j = 0, k = 0;
for (j; j < i && k < dn[i].size(); j++, k++)
if (dn[i][k] != j)
break;
if (j != i)
l.push_back({ p[i], p[j] - p[i] });
}
l.push_back({ p.front(), p.back() - p.front() });
vector<point> pla = plane(l);
double ans = circumference(pla) - dist(p.front(), p.back());
printf("%.5llf\n", ans);
return 0;
}