euclidean_mst.hpp¶
求整数平面点的欧氏最小生成树;通过 Delaunay 候选边避免完全图。
Complexity: Time: O(n log n). Space: O(n).
AC 记录:euclidean_mst。
Implementation¶
当前头文件,省略 include guard;依赖见 #include。
/// @complexity Time: O(n log n).
/// Space: O(n).
#include "atcoder/dsu.hpp"
#include "noya/geometry_base.hpp"
#include <algorithm>
#include <cassert>
#include <cstdint>
#include <numeric>
#include <tuple>
#include <utility>
#include <vector>
namespace noya {
namespace euclidean_mst_internal {
template <class Int = long long, class Int2 = long long> struct VecI2 {
Int x, y;
VecI2() : x(0), y(0) {}
VecI2(std::pair<Int, Int> _p)
: x(std::move(_p.first)), y(std::move(_p.second)) {}
VecI2(Int _x, Int _y) : x(std::move(_x)), y(std::move(_y)) {}
VecI2 &operator+=(VecI2 r) {
x += r.x;
y += r.y;
return *this;
}
VecI2 &operator-=(VecI2 r) {
x -= r.x;
y -= r.y;
return *this;
}
VecI2 &operator*=(Int r) {
x *= r;
y *= r;
return *this;
}
VecI2 operator+(VecI2 r) const { return VecI2(x + r.x, y + r.y); }
VecI2 operator-(VecI2 r) const { return VecI2(x - r.x, y - r.y); }
VecI2 operator*(Int r) const { return VecI2(x * r, y * r); }
VecI2 operator-() const { return VecI2(-x, -y); }
Int2 operator*(VecI2 r) const {
return Int2(x) * Int2(r.x) + Int2(y) * Int2(r.y);
}
Int2 operator^(VecI2 r) const {
return Int2(x) * Int2(r.y) - Int2(y) * Int2(r.x);
}
bool operator<(VecI2 r) const { return x < r.x || (!(r.x < x) && y < r.y); }
Int2 norm() const { return Int2(x) * Int2(x) + Int2(y) * Int2(y); }
static bool compareYX(VecI2 a, VecI2 b) {
return a.y < b.y || (!(b.y < a.y) && a.x < b.x);
}
static bool compareXY(VecI2 a, VecI2 b) {
return a.x < b.x || (!(b.x < a.x) && a.y < b.y);
}
bool operator==(VecI2 r) const { return x == r.x && y == r.y; }
bool operator!=(VecI2 r) const { return x != r.x || y != r.y; }
};
template <class Elem> class CsrArray {
public:
struct ListRange {
using iterator = typename std::vector<Elem>::iterator;
iterator bg, ed;
iterator begin() const { return bg; }
iterator end() const { return ed; }
int size() const { return (int)std::distance(bg, ed); }
Elem &operator[](int i) const { return bg[i]; }
};
struct ConstListRange {
using iterator = typename std::vector<Elem>::const_iterator;
iterator bg, ed;
iterator begin() const { return bg; }
iterator end() const { return ed; }
int size() const { return (int)std::distance(bg, ed); }
const Elem &operator[](int i) const { return bg[i]; }
};
private:
int m_n;
std::vector<Elem> ls;
std::vector<int> po1;
public:
CsrArray() : m_n(0), ls(), po1() {}
static CsrArray Construct(int n, std::vector<std::pair<int, Elem>> xs) {
CsrArray res;
res.m_n = n;
std::vector<int> buf(n + 1, 0);
for (auto &[u, v] : xs) {
++buf[u];
}
for (int i = 1; i <= n; i++)
buf[i] += buf[i - 1];
res.ls.resize(buf[n]);
for (int i = (int)xs.size() - 1; i >= 0; i--) {
res.ls[--buf[xs[i].first]] = std::move(xs[i].second);
}
res.po1 = std::move(buf);
return res;
}
static CsrArray FromRaw(std::vector<Elem> ls1, std::vector<int> pos) {
CsrArray res;
res.m_n = pos.size() - 1;
res.ls = std::move(ls1);
res.po1 = std::move(pos);
return res;
}
ListRange operator[](int u) {
return ListRange{ls.begin() + po1[u], ls.begin() + po1[u + 1]};
}
ConstListRange operator[](int u) const {
return ConstListRange{ls.begin() + po1[u], ls.begin() + po1[u + 1]};
}
int size() const { return m_n; }
int fullSize() const { return (int)ls.size(); }
};
// Int3 must be able to handle the value range :
// |x| <= | (any input - any input) ** 4 * 12 |
template <class Int = long long, class Int2 = long long, class Int3 = Int2>
class DelaunayTriangulation {
public:
using GPos2 = VecI2<Int, Int2>;
struct Edge {
int to;
int ccw;
int cw;
int rev;
bool on = false;
};
private:
static int isDinOABC(GPos2 a, GPos2 b, GPos2 c, GPos2 d) {
a = a - d;
b = b - d;
c = c - d;
auto val = Int3(b ^ c) * Int3(a.norm()) + Int3(c ^ a) * Int3(b.norm()) +
Int3(a ^ b) * Int3(c.norm());
return val > Int3(0) ? 1 : 0;
}
int getOpenAddress() {
if (oA.empty()) {
es.push_back({});
return (int)es.size() - 1;
}
int res = oA.back();
oA.pop_back();
return res;
}
std::pair<int, int> newEdge(int u, int v) {
int euv = getOpenAddress();
int evu = getOpenAddress();
es[euv].ccw = es[euv].cw = euv;
es[evu].ccw = es[evu].cw = evu;
es[euv].to = v;
es[evu].to = u;
es[euv].rev = evu;
es[evu].rev = euv;
es[euv].on = true;
es[evu].on = true;
return {euv, evu};
}
void eraseSingleEdge(int e) {
int ec = es[e].ccw;
int ecw = es[e].cw;
es[ec].cw = ecw;
es[ecw].ccw = ec;
es[e].on = false;
}
void eraseEdgeBidirectional(int e) {
int ex = es[e].rev;
eraseSingleEdge(e);
eraseSingleEdge(ex);
oA.push_back(e);
oA.push_back(ex);
}
void insertCcwAfter(int e, int x) {
int xc = es[x].ccw;
es[e].ccw = xc;
es[xc].cw = e;
es[e].cw = x;
es[x].ccw = e;
}
void insertCwAfter(int e, int x) {
int xcw = es[x].cw;
es[e].cw = xcw;
es[xcw].ccw = e;
es[e].ccw = x;
es[x].cw = e;
}
// move from ab to ac ... is this ccw?
int isCcw(int a, int b, int c) const {
auto ab = pos[b] - pos[a];
auto ac = pos[c] - pos[a];
auto cp = ab ^ ac;
if (0 < cp)
return 1;
if (cp < 0)
return -1;
return 0;
}
std::pair<int, int> goNext(int, int ea) {
int ap = es[ea].to;
int eap = es[es[ea].rev].ccw;
return {ap, eap};
}
std::pair<int, int> goPrev(int, int ea) {
int ap = es[es[ea].cw].to;
int eap = es[es[ea].cw].rev;
return {ap, eap};
}
std::tuple<int, int, int, int> goBottom(int a, int ea, int b, int eb) {
while (true) {
auto [ap, eap] = goPrev(a, ea);
if (isCcw(b, a, ap) > 0) {
std::tie(a, ea) = {ap, eap};
continue;
}
auto [bp, ebp] = goNext(b, eb);
if (isCcw(a, b, bp) < 0) {
std::tie(b, eb) = {bp, ebp};
continue;
}
break;
}
return {a, ea, b, eb};
}
std::pair<int, int> getMaximum(int a, int ea, bool mn) {
std::pair<int, int> ans = {a, ea};
int p = a, ep = ea;
do {
std::tie(p, ep) = goNext(p, ep);
if (mn)
ans = std::min(ans, std::make_pair(p, ep));
else
ans = std::max(ans, std::make_pair(p, ep));
} while (ep != ea);
return ans;
}
bool isDinOABC(int a, int b, int c, int d) {
return isDinOABC(pos[a], pos[b], pos[c], pos[d]);
}
std::pair<int, int> dfs(int a, int ea, int b, int eb) {
std::tie(a, ea) = getMaximum(a, ea, false);
std::tie(b, eb) = getMaximum(b, eb, true);
auto [al, eal, bl, ebl] = goBottom(a, ea, b, eb);
auto [bu, ebu, au, eau] = goBottom(b, eb, a, ea);
ebl = es[ebl].cw;
ebu = es[ebu].cw;
auto [abl, bal] = newEdge(al, bl);
insertCwAfter(abl, eal);
insertCcwAfter(bal, ebl);
if (al == au)
eau = abl;
if (bl == bu)
ebu = bal;
int ap = al, eap = eal;
int bp = bl, ebp = ebl;
while (ap != au || bp != bu) {
int a2 = es[eap].to;
int b2 = es[ebp].to;
int na = es[eap].ccw;
int nb = es[ebp].cw;
if (eap != eau && na != abl) {
int a1 = es[na].to;
if (isDinOABC(ap, bp, a2, a1)) {
eraseEdgeBidirectional(eap);
eap = na;
continue;
}
}
if (ebp != ebu && nb != bal) {
int b1 = es[nb].to;
if (isDinOABC(b2, ap, bp, b1)) {
eraseEdgeBidirectional(ebp);
ebp = nb;
continue;
}
}
bool ca = ebp == ebu;
if (eap != eau && ebp != ebu) {
if (isCcw(ap, bp, b2) < 0)
ca = true;
else if (isCcw(a2, ap, bp) < 0)
ca = false;
else
ca = isDinOABC(ap, bp, b2, a2);
}
if (ca) {
na = es[es[eap].rev].ccw;
auto [hab, hba] = newEdge(a2, bp);
insertCwAfter(hab, na);
insertCcwAfter(hba, ebp);
eap = na;
ap = a2;
} else {
nb = es[es[ebp].rev].cw;
auto [hba, hab] = newEdge(b2, ap);
insertCcwAfter(hba, nb);
insertCwAfter(hab, eap);
ebp = nb;
bp = b2;
}
}
return {al, abl};
}
std::pair<int, int> solveRange(int l, int r) {
if (r - l == 2) {
int u = l;
int v = l + 1;
auto [uv, vu] = newEdge(u, v);
return {u, uv};
}
if (r - l == 3) {
int u = l;
int v = l + 1;
int w = l + 2;
auto [uv, vu] = newEdge(u, v);
auto [vw, wv] = newEdge(v, w);
int ccw = isCcw(u, v, w);
if (ccw == 0) {
insertCcwAfter(vu, vw);
}
if (ccw > 0) {
auto [uw, wu] = newEdge(u, w);
insertCwAfter(uv, uw);
insertCwAfter(vw, vu);
insertCwAfter(wu, wv);
return {u, uv};
}
if (ccw < 0) {
auto [uw, wu] = newEdge(u, w);
insertCcwAfter(uv, uw);
insertCcwAfter(vw, vu);
insertCcwAfter(wu, wv);
return {v, vu};
}
return {u, uv};
}
int m = (l + r) / 2;
auto [a, ea] = solveRange(l, m);
auto [b, eb] = solveRange(m, r);
return dfs(a, ea, b, eb);
}
void solve() {
int sz = (int)pos.size();
if (sz <= 1)
return;
std::vector<int> pi(pos.size());
for (int i = 0; i < (int)pi.size(); i++)
pi[i] = i;
std::stable_sort(pi.begin(), pi.end(), [&](int l, int r) {
return pos[l].x != pos[r].x ? pos[l].x < pos[r].x : pos[l].y < pos[r].y;
});
auto bu1 = pos;
int ptr = 0;
mp.assign(sz, 0);
for (int i = 0; i < sz; i++) {
int v = pi[i];
if (i == 0 || !(bu1[pi[ptr - 1]] == bu1[v])) {
pi[ptr] = v;
pos[ptr++] = bu1[v];
mp[v] = v;
} else {
mp[v] = pi[ptr - 1];
}
}
if (ptr >= 2)
oOE = solveRange(0, ptr).second;
std::swap(pos, bu1);
for (auto &e : es)
e.to = pi[e.to];
}
std::vector<int> oA;
std::vector<GPos2> pos;
std::vector<Edge> es;
std::vector<int> mp;
int oOE = -1;
public:
DelaunayTriangulation() : pos() { solve(); }
DelaunayTriangulation(std::vector<GPos2> xp) : pos(std::move(xp)) { solve(); }
std::vector<std::pair<int, int>> getEdges() const {
std::vector<std::pair<int, int>> res;
for (int e = 0; e < (int)es.size(); e++)
if (es[e].on) {
int re = es[e].rev;
if (e < re)
continue;
res.push_back({es[e].to, es[re].to});
}
for (int v = 0; v < int(mp.size()); v++) {
if (mp[v] != v)
res.push_back({v, mp[v]});
}
return res;
}
};
} // namespace euclidean_mst_internal
/// @brief Return a Euclidean minimum spanning tree on integral planar points.
/// Divide-and-conquer Delaunay triangulation keeps only O(n) candidate edges:
/// the empty-circumcircle property guarantees that every Euclidean MST edge is
/// present. Kruskal on squared lengths then selects the tree; duplicate points
/// are connected by explicit zero-length edges.
inline std::vector<std::pair<int, int>>
euclidean_mst(const std::vector<point<long long>> &pt) {
using internal_point = euclidean_mst_internal::VecI2<long long, long long>;
using triangulation =
euclidean_mst_internal::DelaunayTriangulation<long long, long long,
__int128_t>;
std::vector<internal_point> lhs;
lhs.reserve(pt.size());
for (const auto &va1 : pt) {
lhs.emplace_back(va1.x, va1.y);
}
std::vector<std::pair<int, int>> can =
triangulation(std::move(lhs)).getEdges();
auto sd = [&](const std::pair<int, int> &arc) {
__int128_t dx = __int128_t(pt[arc.first].x) - pt[arc.second].x;
__int128_t dy = __int128_t(pt[arc.first].y) - pt[arc.second].y;
return dx * dx + dy * dy;
};
std::stable_sort(
can.begin(), can.end(),
[&](const auto &a3, const auto &rhs) { return sd(a3) < sd(rhs); });
atcoder::dsu ds1(int(pt.size()));
std::vector<std::pair<int, int>> ret;
ret.reserve(pt.empty() ? 0 : pt.size() - 1);
for (auto arc : can) {
if (ds1.same(arc.first, arc.second)) {
continue;
}
ds1.merge(arc.first, arc.second);
if (arc.first > arc.second) {
std::swap(arc.first, arc.second);
}
ret.push_back(arc);
}
std::sort(ret.begin(), ret.end());
return ret;
}
} // namespace noya
#ifndef NOYA_EUCLIDEAN_MST_HPP
#define NOYA_EUCLIDEAN_MST_HPP 1
/// @complexity Time: O(n log n).
/// Space: O(n).
#include "atcoder/dsu.hpp"
#include "noya/geometry_base.hpp"
#include <algorithm>
#include <cassert>
#include <cstdint>
#include <numeric>
#include <tuple>
#include <utility>
#include <vector>
namespace noya {
namespace euclidean_mst_internal {
template <class Int = long long, class Int2 = long long> struct VecI2 {
Int x, y;
VecI2() : x(0), y(0) {}
VecI2(std::pair<Int, Int> _p)
: x(std::move(_p.first)), y(std::move(_p.second)) {}
VecI2(Int _x, Int _y) : x(std::move(_x)), y(std::move(_y)) {}
VecI2 &operator+=(VecI2 r) {
x += r.x;
y += r.y;
return *this;
}
VecI2 &operator-=(VecI2 r) {
x -= r.x;
y -= r.y;
return *this;
}
VecI2 &operator*=(Int r) {
x *= r;
y *= r;
return *this;
}
VecI2 operator+(VecI2 r) const { return VecI2(x + r.x, y + r.y); }
VecI2 operator-(VecI2 r) const { return VecI2(x - r.x, y - r.y); }
VecI2 operator*(Int r) const { return VecI2(x * r, y * r); }
VecI2 operator-() const { return VecI2(-x, -y); }
Int2 operator*(VecI2 r) const {
return Int2(x) * Int2(r.x) + Int2(y) * Int2(r.y);
}
Int2 operator^(VecI2 r) const {
return Int2(x) * Int2(r.y) - Int2(y) * Int2(r.x);
}
bool operator<(VecI2 r) const { return x < r.x || (!(r.x < x) && y < r.y); }
Int2 norm() const { return Int2(x) * Int2(x) + Int2(y) * Int2(y); }
static bool compareYX(VecI2 a, VecI2 b) {
return a.y < b.y || (!(b.y < a.y) && a.x < b.x);
}
static bool compareXY(VecI2 a, VecI2 b) {
return a.x < b.x || (!(b.x < a.x) && a.y < b.y);
}
bool operator==(VecI2 r) const { return x == r.x && y == r.y; }
bool operator!=(VecI2 r) const { return x != r.x || y != r.y; }
};
template <class Elem> class CsrArray {
public:
struct ListRange {
using iterator = typename std::vector<Elem>::iterator;
iterator bg, ed;
iterator begin() const { return bg; }
iterator end() const { return ed; }
int size() const { return (int)std::distance(bg, ed); }
Elem &operator[](int i) const { return bg[i]; }
};
struct ConstListRange {
using iterator = typename std::vector<Elem>::const_iterator;
iterator bg, ed;
iterator begin() const { return bg; }
iterator end() const { return ed; }
int size() const { return (int)std::distance(bg, ed); }
const Elem &operator[](int i) const { return bg[i]; }
};
private:
int m_n;
std::vector<Elem> ls;
std::vector<int> po1;
public:
CsrArray() : m_n(0), ls(), po1() {}
static CsrArray Construct(int n, std::vector<std::pair<int, Elem>> xs) {
CsrArray res;
res.m_n = n;
std::vector<int> buf(n + 1, 0);
for (auto &[u, v] : xs) {
++buf[u];
}
for (int i = 1; i <= n; i++)
buf[i] += buf[i - 1];
res.ls.resize(buf[n]);
for (int i = (int)xs.size() - 1; i >= 0; i--) {
res.ls[--buf[xs[i].first]] = std::move(xs[i].second);
}
res.po1 = std::move(buf);
return res;
}
static CsrArray FromRaw(std::vector<Elem> ls1, std::vector<int> pos) {
CsrArray res;
res.m_n = pos.size() - 1;
res.ls = std::move(ls1);
res.po1 = std::move(pos);
return res;
}
ListRange operator[](int u) {
return ListRange{ls.begin() + po1[u], ls.begin() + po1[u + 1]};
}
ConstListRange operator[](int u) const {
return ConstListRange{ls.begin() + po1[u], ls.begin() + po1[u + 1]};
}
int size() const { return m_n; }
int fullSize() const { return (int)ls.size(); }
};
// Int3 must be able to handle the value range :
// |x| <= | (any input - any input) ** 4 * 12 |
template <class Int = long long, class Int2 = long long, class Int3 = Int2>
class DelaunayTriangulation {
public:
using GPos2 = VecI2<Int, Int2>;
struct Edge {
int to;
int ccw;
int cw;
int rev;
bool on = false;
};
private:
static int isDinOABC(GPos2 a, GPos2 b, GPos2 c, GPos2 d) {
a = a - d;
b = b - d;
c = c - d;
auto val = Int3(b ^ c) * Int3(a.norm()) + Int3(c ^ a) * Int3(b.norm()) +
Int3(a ^ b) * Int3(c.norm());
return val > Int3(0) ? 1 : 0;
}
int getOpenAddress() {
if (oA.empty()) {
es.push_back({});
return (int)es.size() - 1;
}
int res = oA.back();
oA.pop_back();
return res;
}
std::pair<int, int> newEdge(int u, int v) {
int euv = getOpenAddress();
int evu = getOpenAddress();
es[euv].ccw = es[euv].cw = euv;
es[evu].ccw = es[evu].cw = evu;
es[euv].to = v;
es[evu].to = u;
es[euv].rev = evu;
es[evu].rev = euv;
es[euv].on = true;
es[evu].on = true;
return {euv, evu};
}
void eraseSingleEdge(int e) {
int ec = es[e].ccw;
int ecw = es[e].cw;
es[ec].cw = ecw;
es[ecw].ccw = ec;
es[e].on = false;
}
void eraseEdgeBidirectional(int e) {
int ex = es[e].rev;
eraseSingleEdge(e);
eraseSingleEdge(ex);
oA.push_back(e);
oA.push_back(ex);
}
void insertCcwAfter(int e, int x) {
int xc = es[x].ccw;
es[e].ccw = xc;
es[xc].cw = e;
es[e].cw = x;
es[x].ccw = e;
}
void insertCwAfter(int e, int x) {
int xcw = es[x].cw;
es[e].cw = xcw;
es[xcw].ccw = e;
es[e].ccw = x;
es[x].cw = e;
}
// move from ab to ac ... is this ccw?
int isCcw(int a, int b, int c) const {
auto ab = pos[b] - pos[a];
auto ac = pos[c] - pos[a];
auto cp = ab ^ ac;
if (0 < cp)
return 1;
if (cp < 0)
return -1;
return 0;
}
std::pair<int, int> goNext(int, int ea) {
int ap = es[ea].to;
int eap = es[es[ea].rev].ccw;
return {ap, eap};
}
std::pair<int, int> goPrev(int, int ea) {
int ap = es[es[ea].cw].to;
int eap = es[es[ea].cw].rev;
return {ap, eap};
}
std::tuple<int, int, int, int> goBottom(int a, int ea, int b, int eb) {
while (true) {
auto [ap, eap] = goPrev(a, ea);
if (isCcw(b, a, ap) > 0) {
std::tie(a, ea) = {ap, eap};
continue;
}
auto [bp, ebp] = goNext(b, eb);
if (isCcw(a, b, bp) < 0) {
std::tie(b, eb) = {bp, ebp};
continue;
}
break;
}
return {a, ea, b, eb};
}
std::pair<int, int> getMaximum(int a, int ea, bool mn) {
std::pair<int, int> ans = {a, ea};
int p = a, ep = ea;
do {
std::tie(p, ep) = goNext(p, ep);
if (mn)
ans = std::min(ans, std::make_pair(p, ep));
else
ans = std::max(ans, std::make_pair(p, ep));
} while (ep != ea);
return ans;
}
bool isDinOABC(int a, int b, int c, int d) {
return isDinOABC(pos[a], pos[b], pos[c], pos[d]);
}
std::pair<int, int> dfs(int a, int ea, int b, int eb) {
std::tie(a, ea) = getMaximum(a, ea, false);
std::tie(b, eb) = getMaximum(b, eb, true);
auto [al, eal, bl, ebl] = goBottom(a, ea, b, eb);
auto [bu, ebu, au, eau] = goBottom(b, eb, a, ea);
ebl = es[ebl].cw;
ebu = es[ebu].cw;
auto [abl, bal] = newEdge(al, bl);
insertCwAfter(abl, eal);
insertCcwAfter(bal, ebl);
if (al == au)
eau = abl;
if (bl == bu)
ebu = bal;
int ap = al, eap = eal;
int bp = bl, ebp = ebl;
while (ap != au || bp != bu) {
int a2 = es[eap].to;
int b2 = es[ebp].to;
int na = es[eap].ccw;
int nb = es[ebp].cw;
if (eap != eau && na != abl) {
int a1 = es[na].to;
if (isDinOABC(ap, bp, a2, a1)) {
eraseEdgeBidirectional(eap);
eap = na;
continue;
}
}
if (ebp != ebu && nb != bal) {
int b1 = es[nb].to;
if (isDinOABC(b2, ap, bp, b1)) {
eraseEdgeBidirectional(ebp);
ebp = nb;
continue;
}
}
bool ca = ebp == ebu;
if (eap != eau && ebp != ebu) {
if (isCcw(ap, bp, b2) < 0)
ca = true;
else if (isCcw(a2, ap, bp) < 0)
ca = false;
else
ca = isDinOABC(ap, bp, b2, a2);
}
if (ca) {
na = es[es[eap].rev].ccw;
auto [hab, hba] = newEdge(a2, bp);
insertCwAfter(hab, na);
insertCcwAfter(hba, ebp);
eap = na;
ap = a2;
} else {
nb = es[es[ebp].rev].cw;
auto [hba, hab] = newEdge(b2, ap);
insertCcwAfter(hba, nb);
insertCwAfter(hab, eap);
ebp = nb;
bp = b2;
}
}
return {al, abl};
}
std::pair<int, int> solveRange(int l, int r) {
if (r - l == 2) {
int u = l;
int v = l + 1;
auto [uv, vu] = newEdge(u, v);
return {u, uv};
}
if (r - l == 3) {
int u = l;
int v = l + 1;
int w = l + 2;
auto [uv, vu] = newEdge(u, v);
auto [vw, wv] = newEdge(v, w);
int ccw = isCcw(u, v, w);
if (ccw == 0) {
insertCcwAfter(vu, vw);
}
if (ccw > 0) {
auto [uw, wu] = newEdge(u, w);
insertCwAfter(uv, uw);
insertCwAfter(vw, vu);
insertCwAfter(wu, wv);
return {u, uv};
}
if (ccw < 0) {
auto [uw, wu] = newEdge(u, w);
insertCcwAfter(uv, uw);
insertCcwAfter(vw, vu);
insertCcwAfter(wu, wv);
return {v, vu};
}
return {u, uv};
}
int m = (l + r) / 2;
auto [a, ea] = solveRange(l, m);
auto [b, eb] = solveRange(m, r);
return dfs(a, ea, b, eb);
}
void solve() {
int sz = (int)pos.size();
if (sz <= 1)
return;
std::vector<int> pi(pos.size());
for (int i = 0; i < (int)pi.size(); i++)
pi[i] = i;
std::stable_sort(pi.begin(), pi.end(), [&](int l, int r) {
return pos[l].x != pos[r].x ? pos[l].x < pos[r].x : pos[l].y < pos[r].y;
});
auto bu1 = pos;
int ptr = 0;
mp.assign(sz, 0);
for (int i = 0; i < sz; i++) {
int v = pi[i];
if (i == 0 || !(bu1[pi[ptr - 1]] == bu1[v])) {
pi[ptr] = v;
pos[ptr++] = bu1[v];
mp[v] = v;
} else {
mp[v] = pi[ptr - 1];
}
}
if (ptr >= 2)
oOE = solveRange(0, ptr).second;
std::swap(pos, bu1);
for (auto &e : es)
e.to = pi[e.to];
}
std::vector<int> oA;
std::vector<GPos2> pos;
std::vector<Edge> es;
std::vector<int> mp;
int oOE = -1;
public:
DelaunayTriangulation() : pos() { solve(); }
DelaunayTriangulation(std::vector<GPos2> xp) : pos(std::move(xp)) { solve(); }
std::vector<std::pair<int, int>> getEdges() const {
std::vector<std::pair<int, int>> res;
for (int e = 0; e < (int)es.size(); e++)
if (es[e].on) {
int re = es[e].rev;
if (e < re)
continue;
res.push_back({es[e].to, es[re].to});
}
for (int v = 0; v < int(mp.size()); v++) {
if (mp[v] != v)
res.push_back({v, mp[v]});
}
return res;
}
};
} // namespace euclidean_mst_internal
/// @brief Return a Euclidean minimum spanning tree on integral planar points.
/// Divide-and-conquer Delaunay triangulation keeps only O(n) candidate edges:
/// the empty-circumcircle property guarantees that every Euclidean MST edge is
/// present. Kruskal on squared lengths then selects the tree; duplicate points
/// are connected by explicit zero-length edges.
inline std::vector<std::pair<int, int>>
euclidean_mst(const std::vector<point<long long>> &pt) {
using internal_point = euclidean_mst_internal::VecI2<long long, long long>;
using triangulation =
euclidean_mst_internal::DelaunayTriangulation<long long, long long,
__int128_t>;
std::vector<internal_point> lhs;
lhs.reserve(pt.size());
for (const auto &va1 : pt) {
lhs.emplace_back(va1.x, va1.y);
}
std::vector<std::pair<int, int>> can =
triangulation(std::move(lhs)).getEdges();
auto sd = [&](const std::pair<int, int> &arc) {
__int128_t dx = __int128_t(pt[arc.first].x) - pt[arc.second].x;
__int128_t dy = __int128_t(pt[arc.first].y) - pt[arc.second].y;
return dx * dx + dy * dy;
};
std::stable_sort(
can.begin(), can.end(),
[&](const auto &a3, const auto &rhs) { return sd(a3) < sd(rhs); });
atcoder::dsu ds1(int(pt.size()));
std::vector<std::pair<int, int>> ret;
ret.reserve(pt.empty() ? 0 : pt.size() - 1);
for (auto arc : can) {
if (ds1.same(arc.first, arc.second)) {
continue;
}
ds1.merge(arc.first, arc.second);
if (arc.first > arc.second) {
std::swap(arc.first, arc.second);
}
ret.push_back(arc);
}
std::sort(ret.begin(), ret.end());
return ret;
}
} // namespace noya
#endif // NOYA_EUCLIDEAN_MST_HPP
#include <algorithm>
#include <cassert>
#include <cmath>
#include <cstdint>
#include <numeric>
#include <optional>
#include <tuple>
#include <utility>
#include <vector>
/// @complexity Time: O(n log n).
/// Space: O(n).
namespace atcoder {
// Implement (union by size) + (path compression)
// Reference:
// Zvi Galil and Giuseppe F. Italiano,
// Data structures and algorithms for disjoint set union problems
struct dsu {
public:
dsu() : _n(0) {}
explicit dsu(int n) : _n(n), parent_or_size(n, -1) {}
int merge(int a, int b) {
assert(0 <= a && a < _n);
assert(0 <= b && b < _n);
int x = leader(a), y = leader(b);
if (x == y) return x;
if (-parent_or_size[x] < -parent_or_size[y]) std::swap(x, y);
parent_or_size[x] += parent_or_size[y];
parent_or_size[y] = x;
return x;
}
bool same(int a, int b) {
assert(0 <= a && a < _n);
assert(0 <= b && b < _n);
return leader(a) == leader(b);
}
int leader(int a) {
assert(0 <= a && a < _n);
return _leader(a);
}
int size(int a) {
assert(0 <= a && a < _n);
return -parent_or_size[leader(a)];
}
std::vector<std::vector<int>> groups() {
std::vector<int> leader_buf(_n), group_size(_n);
for (int i = 0; i < _n; i++) {
leader_buf[i] = leader(i);
group_size[leader_buf[i]]++;
}
std::vector<std::vector<int>> result(_n);
for (int i = 0; i < _n; i++) {
result[i].reserve(group_size[i]);
}
for (int i = 0; i < _n; i++) {
result[leader_buf[i]].push_back(i);
}
result.erase(
std::remove_if(result.begin(), result.end(),
[&](const std::vector<int>& v) { return v.empty(); }),
result.end());
return result;
}
private:
int _n;
// root node: -1 * component size
// otherwise: parent
std::vector<int> parent_or_size;
int _leader(int a) {
if (parent_or_size[a] < 0) return a;
return parent_or_size[a] = _leader(parent_or_size[a]);
}
};
} // namespace atcoder
/// @complexity Time: O(1) per primitive; O(n log n) for convex hull.
/// Space: O(1) per primitive and O(n) for hull construction.
namespace noya {
/// @brief Two-dimensional point with vector arithmetic and lexicographic order.
template <class T> struct point {
T x{};
T y{};
point() = default;
point(T x_, T y_) : x(x_), y(y_) {}
point &operator+=(const point &rhs) {
x += rhs.x;
y += rhs.y;
return *this;
}
point &operator-=(const point &rhs) {
x -= rhs.x;
y -= rhs.y;
return *this;
}
point &operator*=(const T &scl) {
x *= scl;
y *= scl;
return *this;
}
point &operator/=(const T &scl) {
x /= scl;
y /= scl;
return *this;
}
friend point operator+(point l, const point &r) { return l += r; }
friend point operator-(point l, const point &r) { return l -= r; }
friend point operator*(point val, const T &scl) { return val *= scl; }
friend point operator*(const T &scl, point val) { return val *= scl; }
friend point operator/(point val, const T &scl) { return val /= scl; }
friend bool operator==(const point &, const point &) = default;
friend bool operator<(const point &l, const point &r) {
return l.x < r.x || (l.x == r.x && l.y < r.y);
}
};
/// @brief Circle represented by a center and a nonnegative radius.
template <class Real> struct circle {
point<Real> o;
Real r1{};
};
/// @brief Return the dot product of two vectors.
template <class T> T dot(const point<T> &a, const point<T> &b) {
return a.x * b.x + a.y * b.y;
}
/// @brief Return the signed cross product of two vectors.
template <class T> T cross(const point<T> &a, const point<T> &b) {
return a.x * b.y - a.y * b.x;
}
/// @brief Return cross(a - o1, b - o1).
template <class T>
T cross(const point<T> &o1, const point<T> &a, const point<T> &b) {
return cross(a - o1, b - o1);
}
/// @brief Return the squared Euclidean norm.
template <class T> T norm2(const point<T> &val) { return dot(val, val); }
/// @brief Compare a value with zero using an optional absolute tolerance.
template <class T> int sign(const T &val, const T &eps = T{}) {
return (val > eps) - (val < -eps);
}
/// @brief Return -1, 0, or 1 for a clockwise, collinear, or counter-clockwise
/// turn.
template <class T>
int orientation(const point<T> &a, const point<T> &b, const point<T> &c,
const T &eps = T{}) {
return sign(cross(a, b, c), eps);
}
/// @brief Test whether p lies on the closed segment [a, b].
template <class T>
bool on_segment(const point<T> &p, const point<T> &a, const point<T> &b,
const T &eps = T{}) {
if (orientation(a, b, p, eps) != 0) {
return false;
}
return std::min(a.x, b.x) - eps <= p.x && p.x <= std::max(a.x, b.x) + eps &&
std::min(a.y, b.y) - eps <= p.y && p.y <= std::max(a.y, b.y) + eps;
}
/// @brief Test whether the closed segments [a, b] and [c, d] intersect.
template <class T>
bool segments_intersect(const point<T> &a, const point<T> &b, const point<T> &c,
const point<T> &d, const T &eps = T{}) {
int s1 = orientation(a, b, c, eps);
int s2 = orientation(a, b, d, eps);
int s3 = orientation(c, d, a, eps);
int s4 = orientation(c, d, b, eps);
if (s1 == 0 && on_segment(c, a, b, eps)) {
return true;
}
if (s2 == 0 && on_segment(d, a, b, eps)) {
return true;
}
if (s3 == 0 && on_segment(a, c, d, eps)) {
return true;
}
if (s4 == 0 && on_segment(b, c, d, eps)) {
return true;
}
return s1 * s2 < 0 && s3 * s4 < 0;
}
/// @brief Intersect the infinite lines through (a, b) and (c, d), returning
/// nullopt when they are parallel or coincident.
template <class T>
std::optional<point<long double>>
line_intersection(const point<T> &a, const point<T> &b, const point<T> &c,
const point<T> &d, long double eps = 0) {
point<long double> lhs{static_cast<long double>(a.x),
static_cast<long double>(a.y)};
point<long double> b1{static_cast<long double>(b.x),
static_cast<long double>(b.y)};
point<long double> z{static_cast<long double>(c.x),
static_cast<long double>(c.y)};
point<long double> d1{static_cast<long double>(d.x),
static_cast<long double>(d.y)};
point<long double> da = b1 - lhs;
point<long double> db = d1 - z;
long double den = cross(da, db);
if (std::abs(den) <= eps) {
return std::nullopt;
}
long double rat = cross(z - lhs, db) / den;
return lhs + da * rat;
}
/// @brief Return the convex hull in counter-clockwise order without repetition.
template <class T>
std::vector<point<T>> convex_hull(std::vector<point<T>> pt, bool kc = false) {
std::sort(pt.begin(), pt.end());
pt.erase(std::unique(pt.begin(), pt.end()), pt.end());
if (pt.size() <= 1) {
return pt;
}
bool col = true;
for (int i = 2; i < int(pt.size()); i++) {
col &= orientation(pt[0], pt[1], pt[i]) == 0;
}
if (kc && col) {
return pt;
}
std::vector<point<T>> lo, hi;
for (const point<T> &p : pt) {
while (lo.size() >= 2) {
int rot = orientation(lo[lo.size() - 2], lo.back(), p);
if (rot > 0 || (kc && rot == 0)) {
break;
}
lo.pop_back();
}
lo.push_back(p);
}
for (auto it = pt.rbegin(); it != pt.rend(); ++it) {
while (hi.size() >= 2) {
int rot = orientation(hi[hi.size() - 2], hi.back(), *it);
if (rot > 0 || (kc && rot == 0)) {
break;
}
hi.pop_back();
}
hi.push_back(*it);
}
lo.pop_back();
hi.pop_back();
lo.insert(lo.end(), hi.begin(), hi.end());
return lo;
}
/// @brief Return twice the signed area of a polygon.
template <class T> T polygon_area2(const std::vector<point<T>> &pg) {
T res{};
for (int i = 0; i < int(pg.size()); i++) {
res += cross(pg[i], pg[(i + 1) % pg.size()]);
}
return res;
}
/// @brief Classify a point relative to a polygon: -1 outside, 0 boundary, 1
/// inside.
template <class T>
int point_in_polygon(const point<T> &p, const std::vector<point<T>> &pg) {
bool in = false;
for (int i = 0; i < int(pg.size()); i++) {
point<T> a = pg[i];
point<T> b = pg[(i + 1) % pg.size()];
if (on_segment(p, a, b)) {
return 0;
}
if (a.y <= p.y && p.y < b.y && orientation(a, b, p) > 0) {
in = !in;
}
if (b.y <= p.y && p.y < a.y && orientation(a, b, p) < 0) {
in = !in;
}
}
return in ? 1 : -1;
}
/// @brief Return the Euclidean distance between two points.
template <class T> long double distance(const point<T> &a, const point<T> &b) {
return std::hypot(
static_cast<long double>(a.x) - static_cast<long double>(b.x),
static_cast<long double>(a.y) - static_cast<long double>(b.y));
}
} // namespace noya
namespace noya {
namespace euclidean_mst_internal {
template <class Int = long long, class Int2 = long long> struct VecI2 {
Int x, y;
VecI2() : x(0), y(0) {}
VecI2(std::pair<Int, Int> _p)
: x(std::move(_p.first)), y(std::move(_p.second)) {}
VecI2(Int _x, Int _y) : x(std::move(_x)), y(std::move(_y)) {}
VecI2 &operator+=(VecI2 r) {
x += r.x;
y += r.y;
return *this;
}
VecI2 &operator-=(VecI2 r) {
x -= r.x;
y -= r.y;
return *this;
}
VecI2 &operator*=(Int r) {
x *= r;
y *= r;
return *this;
}
VecI2 operator+(VecI2 r) const { return VecI2(x + r.x, y + r.y); }
VecI2 operator-(VecI2 r) const { return VecI2(x - r.x, y - r.y); }
VecI2 operator*(Int r) const { return VecI2(x * r, y * r); }
VecI2 operator-() const { return VecI2(-x, -y); }
Int2 operator*(VecI2 r) const {
return Int2(x) * Int2(r.x) + Int2(y) * Int2(r.y);
}
Int2 operator^(VecI2 r) const {
return Int2(x) * Int2(r.y) - Int2(y) * Int2(r.x);
}
bool operator<(VecI2 r) const { return x < r.x || (!(r.x < x) && y < r.y); }
Int2 norm() const { return Int2(x) * Int2(x) + Int2(y) * Int2(y); }
static bool compareYX(VecI2 a, VecI2 b) {
return a.y < b.y || (!(b.y < a.y) && a.x < b.x);
}
static bool compareXY(VecI2 a, VecI2 b) {
return a.x < b.x || (!(b.x < a.x) && a.y < b.y);
}
bool operator==(VecI2 r) const { return x == r.x && y == r.y; }
bool operator!=(VecI2 r) const { return x != r.x || y != r.y; }
};
template <class Elem> class CsrArray {
public:
struct ListRange {
using iterator = typename std::vector<Elem>::iterator;
iterator bg, ed;
iterator begin() const { return bg; }
iterator end() const { return ed; }
int size() const { return (int)std::distance(bg, ed); }
Elem &operator[](int i) const { return bg[i]; }
};
struct ConstListRange {
using iterator = typename std::vector<Elem>::const_iterator;
iterator bg, ed;
iterator begin() const { return bg; }
iterator end() const { return ed; }
int size() const { return (int)std::distance(bg, ed); }
const Elem &operator[](int i) const { return bg[i]; }
};
private:
int m_n;
std::vector<Elem> ls;
std::vector<int> po1;
public:
CsrArray() : m_n(0), ls(), po1() {}
static CsrArray Construct(int n, std::vector<std::pair<int, Elem>> xs) {
CsrArray res;
res.m_n = n;
std::vector<int> buf(n + 1, 0);
for (auto &[u, v] : xs) {
++buf[u];
}
for (int i = 1; i <= n; i++)
buf[i] += buf[i - 1];
res.ls.resize(buf[n]);
for (int i = (int)xs.size() - 1; i >= 0; i--) {
res.ls[--buf[xs[i].first]] = std::move(xs[i].second);
}
res.po1 = std::move(buf);
return res;
}
static CsrArray FromRaw(std::vector<Elem> ls1, std::vector<int> pos) {
CsrArray res;
res.m_n = pos.size() - 1;
res.ls = std::move(ls1);
res.po1 = std::move(pos);
return res;
}
ListRange operator[](int u) {
return ListRange{ls.begin() + po1[u], ls.begin() + po1[u + 1]};
}
ConstListRange operator[](int u) const {
return ConstListRange{ls.begin() + po1[u], ls.begin() + po1[u + 1]};
}
int size() const { return m_n; }
int fullSize() const { return (int)ls.size(); }
};
// Int3 must be able to handle the value range :
// |x| <= | (any input - any input) ** 4 * 12 |
template <class Int = long long, class Int2 = long long, class Int3 = Int2>
class DelaunayTriangulation {
public:
using GPos2 = VecI2<Int, Int2>;
struct Edge {
int to;
int ccw;
int cw;
int rev;
bool on = false;
};
private:
static int isDinOABC(GPos2 a, GPos2 b, GPos2 c, GPos2 d) {
a = a - d;
b = b - d;
c = c - d;
auto val = Int3(b ^ c) * Int3(a.norm()) + Int3(c ^ a) * Int3(b.norm()) +
Int3(a ^ b) * Int3(c.norm());
return val > Int3(0) ? 1 : 0;
}
int getOpenAddress() {
if (oA.empty()) {
es.push_back({});
return (int)es.size() - 1;
}
int res = oA.back();
oA.pop_back();
return res;
}
std::pair<int, int> newEdge(int u, int v) {
int euv = getOpenAddress();
int evu = getOpenAddress();
es[euv].ccw = es[euv].cw = euv;
es[evu].ccw = es[evu].cw = evu;
es[euv].to = v;
es[evu].to = u;
es[euv].rev = evu;
es[evu].rev = euv;
es[euv].on = true;
es[evu].on = true;
return {euv, evu};
}
void eraseSingleEdge(int e) {
int ec = es[e].ccw;
int ecw = es[e].cw;
es[ec].cw = ecw;
es[ecw].ccw = ec;
es[e].on = false;
}
void eraseEdgeBidirectional(int e) {
int ex = es[e].rev;
eraseSingleEdge(e);
eraseSingleEdge(ex);
oA.push_back(e);
oA.push_back(ex);
}
void insertCcwAfter(int e, int x) {
int xc = es[x].ccw;
es[e].ccw = xc;
es[xc].cw = e;
es[e].cw = x;
es[x].ccw = e;
}
void insertCwAfter(int e, int x) {
int xcw = es[x].cw;
es[e].cw = xcw;
es[xcw].ccw = e;
es[e].ccw = x;
es[x].cw = e;
}
// move from ab to ac ... is this ccw?
int isCcw(int a, int b, int c) const {
auto ab = pos[b] - pos[a];
auto ac = pos[c] - pos[a];
auto cp = ab ^ ac;
if (0 < cp)
return 1;
if (cp < 0)
return -1;
return 0;
}
std::pair<int, int> goNext(int, int ea) {
int ap = es[ea].to;
int eap = es[es[ea].rev].ccw;
return {ap, eap};
}
std::pair<int, int> goPrev(int, int ea) {
int ap = es[es[ea].cw].to;
int eap = es[es[ea].cw].rev;
return {ap, eap};
}
std::tuple<int, int, int, int> goBottom(int a, int ea, int b, int eb) {
while (true) {
auto [ap, eap] = goPrev(a, ea);
if (isCcw(b, a, ap) > 0) {
std::tie(a, ea) = {ap, eap};
continue;
}
auto [bp, ebp] = goNext(b, eb);
if (isCcw(a, b, bp) < 0) {
std::tie(b, eb) = {bp, ebp};
continue;
}
break;
}
return {a, ea, b, eb};
}
std::pair<int, int> getMaximum(int a, int ea, bool mn) {
std::pair<int, int> ans = {a, ea};
int p = a, ep = ea;
do {
std::tie(p, ep) = goNext(p, ep);
if (mn)
ans = std::min(ans, std::make_pair(p, ep));
else
ans = std::max(ans, std::make_pair(p, ep));
} while (ep != ea);
return ans;
}
bool isDinOABC(int a, int b, int c, int d) {
return isDinOABC(pos[a], pos[b], pos[c], pos[d]);
}
std::pair<int, int> dfs(int a, int ea, int b, int eb) {
std::tie(a, ea) = getMaximum(a, ea, false);
std::tie(b, eb) = getMaximum(b, eb, true);
auto [al, eal, bl, ebl] = goBottom(a, ea, b, eb);
auto [bu, ebu, au, eau] = goBottom(b, eb, a, ea);
ebl = es[ebl].cw;
ebu = es[ebu].cw;
auto [abl, bal] = newEdge(al, bl);
insertCwAfter(abl, eal);
insertCcwAfter(bal, ebl);
if (al == au)
eau = abl;
if (bl == bu)
ebu = bal;
int ap = al, eap = eal;
int bp = bl, ebp = ebl;
while (ap != au || bp != bu) {
int a2 = es[eap].to;
int b2 = es[ebp].to;
int na = es[eap].ccw;
int nb = es[ebp].cw;
if (eap != eau && na != abl) {
int a1 = es[na].to;
if (isDinOABC(ap, bp, a2, a1)) {
eraseEdgeBidirectional(eap);
eap = na;
continue;
}
}
if (ebp != ebu && nb != bal) {
int b1 = es[nb].to;
if (isDinOABC(b2, ap, bp, b1)) {
eraseEdgeBidirectional(ebp);
ebp = nb;
continue;
}
}
bool ca = ebp == ebu;
if (eap != eau && ebp != ebu) {
if (isCcw(ap, bp, b2) < 0)
ca = true;
else if (isCcw(a2, ap, bp) < 0)
ca = false;
else
ca = isDinOABC(ap, bp, b2, a2);
}
if (ca) {
na = es[es[eap].rev].ccw;
auto [hab, hba] = newEdge(a2, bp);
insertCwAfter(hab, na);
insertCcwAfter(hba, ebp);
eap = na;
ap = a2;
} else {
nb = es[es[ebp].rev].cw;
auto [hba, hab] = newEdge(b2, ap);
insertCcwAfter(hba, nb);
insertCwAfter(hab, eap);
ebp = nb;
bp = b2;
}
}
return {al, abl};
}
std::pair<int, int> solveRange(int l, int r) {
if (r - l == 2) {
int u = l;
int v = l + 1;
auto [uv, vu] = newEdge(u, v);
return {u, uv};
}
if (r - l == 3) {
int u = l;
int v = l + 1;
int w = l + 2;
auto [uv, vu] = newEdge(u, v);
auto [vw, wv] = newEdge(v, w);
int ccw = isCcw(u, v, w);
if (ccw == 0) {
insertCcwAfter(vu, vw);
}
if (ccw > 0) {
auto [uw, wu] = newEdge(u, w);
insertCwAfter(uv, uw);
insertCwAfter(vw, vu);
insertCwAfter(wu, wv);
return {u, uv};
}
if (ccw < 0) {
auto [uw, wu] = newEdge(u, w);
insertCcwAfter(uv, uw);
insertCcwAfter(vw, vu);
insertCcwAfter(wu, wv);
return {v, vu};
}
return {u, uv};
}
int m = (l + r) / 2;
auto [a, ea] = solveRange(l, m);
auto [b, eb] = solveRange(m, r);
return dfs(a, ea, b, eb);
}
void solve() {
int sz = (int)pos.size();
if (sz <= 1)
return;
std::vector<int> pi(pos.size());
for (int i = 0; i < (int)pi.size(); i++)
pi[i] = i;
std::stable_sort(pi.begin(), pi.end(), [&](int l, int r) {
return pos[l].x != pos[r].x ? pos[l].x < pos[r].x : pos[l].y < pos[r].y;
});
auto bu1 = pos;
int ptr = 0;
mp.assign(sz, 0);
for (int i = 0; i < sz; i++) {
int v = pi[i];
if (i == 0 || !(bu1[pi[ptr - 1]] == bu1[v])) {
pi[ptr] = v;
pos[ptr++] = bu1[v];
mp[v] = v;
} else {
mp[v] = pi[ptr - 1];
}
}
if (ptr >= 2)
oOE = solveRange(0, ptr).second;
std::swap(pos, bu1);
for (auto &e : es)
e.to = pi[e.to];
}
std::vector<int> oA;
std::vector<GPos2> pos;
std::vector<Edge> es;
std::vector<int> mp;
int oOE = -1;
public:
DelaunayTriangulation() : pos() { solve(); }
DelaunayTriangulation(std::vector<GPos2> xp) : pos(std::move(xp)) { solve(); }
std::vector<std::pair<int, int>> getEdges() const {
std::vector<std::pair<int, int>> res;
for (int e = 0; e < (int)es.size(); e++)
if (es[e].on) {
int re = es[e].rev;
if (e < re)
continue;
res.push_back({es[e].to, es[re].to});
}
for (int v = 0; v < int(mp.size()); v++) {
if (mp[v] != v)
res.push_back({v, mp[v]});
}
return res;
}
};
} // namespace euclidean_mst_internal
/// @brief Return a Euclidean minimum spanning tree on integral planar points.
/// Divide-and-conquer Delaunay triangulation keeps only O(n) candidate edges:
/// the empty-circumcircle property guarantees that every Euclidean MST edge is
/// present. Kruskal on squared lengths then selects the tree; duplicate points
/// are connected by explicit zero-length edges.
inline std::vector<std::pair<int, int>>
euclidean_mst(const std::vector<point<long long>> &pt) {
using internal_point = euclidean_mst_internal::VecI2<long long, long long>;
using triangulation =
euclidean_mst_internal::DelaunayTriangulation<long long, long long,
__int128_t>;
std::vector<internal_point> lhs;
lhs.reserve(pt.size());
for (const auto &va1 : pt) {
lhs.emplace_back(va1.x, va1.y);
}
std::vector<std::pair<int, int>> can =
triangulation(std::move(lhs)).getEdges();
auto sd = [&](const std::pair<int, int> &arc) {
__int128_t dx = __int128_t(pt[arc.first].x) - pt[arc.second].x;
__int128_t dy = __int128_t(pt[arc.first].y) - pt[arc.second].y;
return dx * dx + dy * dy;
};
std::stable_sort(
can.begin(), can.end(),
[&](const auto &a3, const auto &rhs) { return sd(a3) < sd(rhs); });
atcoder::dsu ds1(int(pt.size()));
std::vector<std::pair<int, int>> ret;
ret.reserve(pt.empty() ? 0 : pt.size() - 1);
for (auto arc : can) {
if (ds1.same(arc.first, arc.second)) {
continue;
}
ds1.merge(arc.first, arc.second);
if (arc.first > arc.second) {
std::swap(arc.first, arc.second);
}
ret.push_back(arc);
}
std::sort(ret.begin(), ret.end());
return ret;
}
} // namespace noya