Delaunay 三角剖分
终于写完了,贴个代码先。题目是 公路修建。
只写了 Bowyer-Watson 和 Guibas-Stolfi。
Bowyer-Watson 在网上看了一圈找不到有 OI 选手写,看着一堆工程码风不知道是什么意思,只好自己琢磨,写了好久。写的时候还用到了在学 Guibas-Stolfi 时学到的,但没在 Guibas-Stolfi 里用到的 “三角形链表” 技巧。
花了整整两天是写出来了,代码可读性几乎为零,而且常数特大。但网上说复杂度可以做到 \(\mathcal{O}(n \sqrt n)\),我就不知道怎么做。目前瓶颈在于:对于一张三角剖分好的图,求一个点在哪一个三角形内,要求复杂度不超过 \(\mathcal{O}(\sqrt n)\)。这玩意我只会 \(\mathcal{O}(n)\) 求。求各位大佬指教。
Bowyer-Watson:
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <utility>
#include <vector>
const int MaxN = 5005;
const int MaxF = 200000;
const double eps = 1e-9;
#ifdef Tweetuzki
#define debug(arg...) fprintf(stderr, arg)
#else
#define debug(arg...) void(0)
#endif
typedef struct vec_t {
double x, y;
vec_t(double _x = 0, double _y = 0) { x = _x, y = _y; }
inline friend vec_t operator+(const vec_t &a, const vec_t &b) { return vec_t(a.x + b.x, a.y + b.y); }
inline friend vec_t operator-(const vec_t &a, const vec_t &b) { return vec_t(a.x - b.x, a.y - b.y); }
inline friend vec_t operator*(const vec_t &a, double k) { return vec_t(a.x * k, a.y * k); }
inline friend double dot(const vec_t &a, const vec_t &b) { return a.x * b.x + a.y * b.y; }
inline friend double cross(const vec_t &a, const vec_t &b) { return a.x * b.y - a.y * b.x; }
inline friend double mod(const vec_t &a) { return sqrt(a.x * a.x + a.y * a.y); }
inline void shake() {
if (rand() % 2 == 0) x += 1.0 * rand() / RAND_MAX * eps;
else x -= 1.0 * rand() / RAND_MAX * eps;
if (rand() % 2 == 0) y += 1.0 * rand() / RAND_MAX * eps;
else y -= 1.0 * rand() / RAND_MAX * eps;
}
} node_t;
typedef struct vec3_t {
double x, y, z;
vec3_t(double _x = 0, double _y = 0, double _z = 0) { x = _x, y = _y, z = _z; }
inline friend vec3_t operator+(const vec3_t &a, const vec3_t &b) { return vec3_t(a.x + b.x, a.y + b.y, a.z + b.z); }
inline friend vec3_t operator-(const vec3_t &a, const vec3_t &b) { return vec3_t(a.x - b.x, a.y - b.y, a.z - b.z); }
inline friend vec3_t operator*(const vec3_t &a, double k) { return vec3_t(a.x * k, a.y * k, a.z * k); }
inline friend vec3_t cross(const vec3_t &a, const vec3_t &b) { return vec3_t(a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x); }
inline friend double dot(const vec3_t &a, const vec3_t &b) { return a.x * b.x + a.y * b.y + a.z * b.z; }
} node3_t;
struct triangle_t {
int a, b, c;
int la, lb, lc;
triangle_t(int _a = 0, int _b = 0, int _c = 0, int _la = 0, int _lb = 0, int _lc = 0) {
a = _a, b = _b, c = _c;
la = _la, lb = _lb, lc = _lc;
}
};
struct edge_t {
int u, v;
double w;
edge_t(int _u = 0, int _v = 0, double _w = 0) { u = _u, v = _v, w = _w; }
inline friend bool operator<(const edge_t &a, const edge_t &b) { return a.w < b.w; }
};
int N, CntF;
node_t A[MaxN + 5];
triangle_t F[MaxF + 5];
bool Del[MaxF + 5];
inline vec3_t mapping(const vec_t &a) { return vec3_t(a.x, a.y, a.x * a.x + a.y * a.y); }
inline bool inCircumcircle(const node_t &a, const node_t &b, const node_t &c, const node_t &p) {
node3_t _a = mapping(a), _b = mapping(b), _c = mapping(c), _p = mapping(p);
if (cross(b - a, c - a) < 0) std::swap(_b, _c);
node3_t normal = cross(_b - _a, _c - _a);
if (dot(normal, _p - _a) > eps) return false;
else return true;
}
inline bool inCircumcircle(const triangle_t &t, const node_t &p) { return inCircumcircle(A[t.a], A[t.b], A[t.c], p); }
void init() {
scanf("%d", &N);
for (int i = 1; i <= N; ++i) {
scanf("%lf %lf", &A[i].x, &A[i].y);
A[i].shake();
}
A[N + 1] = node_t(-1e6, -1e6), A[N + 2] = node_t(-1e6, 1e6);
A[N + 3] = node_t(1e6, -1e6), A[N + 4] = node_t(1e6, 1e6);
F[++CntF] = triangle_t(N + 1, N + 3, N + 2, 2, 0, 0);
F[++CntF] = triangle_t(N + 4, N + 2, N + 3, 1, 0, 0);
}
std::vector< std::pair<int, int> > Lk[MaxN + 5];
int To[MaxN + 5], Tof[MaxN + 5];
int NodeStk[MaxN + 5], NodeTp;
void dfs(int u, int f, int beg) {
debug("dfs(%d, %d, %d)\n", u, f, beg);
if (u == beg) {
if (f != 0) return;
NodeStk[NodeTp++] = u;
auto p = *(Lk[u].begin());
To[u] = p.first;
Tof[u] = p.second;
dfs(p.first, u, beg);
} else {
NodeStk[NodeTp++] = u;
for (auto p : Lk[u]) {
if (p.first == f) continue;
To[u] = p.first;
Tof[u] = p.second;
dfs(p.first, u, beg);
}
}
}
inline bool cmp(int a, int b, int c, int d) {
if (a == c && b == d) return true;
if (a == d && b == c) return true;
return false;
}
inline void insert(int insertNode) {
static int stk[MaxF + 5];
int tp = 0;
for (int i = 1; i <= CntF; ++i)
if (Del[i] == false && inCircumcircle(F[i], A[insertNode]) == true) {
stk[++tp] = i;
Del[i] = true;
}
for (int i = 1; i <= tp; ++i) debug("stk[%d] = %d\n", i, stk[i]);
static int e[MaxF + 5][3]; int cnte = 0;
for (int i = 1; i <= tp; ++i) {
int x = stk[i];
if (F[x].la == 0) {
cnte++;
e[cnte][0] = F[x].b, e[cnte][1] = F[x].c, e[cnte][2] = 0;
} else if (inCircumcircle(F[F[x].la], A[insertNode]) == false) {
int y = F[x].la;
cnte++;
if (F[y].la == x) {
F[y].la = -1;
e[cnte][0] = F[y].b, e[cnte][1] = F[y].c, e[cnte][2] = y;
} else if (F[y].lb == x) {
F[y].lb = -1;
e[cnte][0] = F[y].a, e[cnte][1] = F[y].c, e[cnte][2] = y;
} else {
F[y].lc = -1;
e[cnte][0] = F[y].a, e[cnte][1] = F[y].b, e[cnte][2] = y;
}
}
if (F[x].lb == 0) {
cnte++;
e[cnte][0] = F[x].a, e[cnte][1] = F[x].c, e[cnte][2] = 0;
} else if (inCircumcircle(F[F[x].lb], A[insertNode]) == false) {
int y = F[x].lb;
cnte++;
if (F[y].la == x) {
F[y].la = -1;
e[cnte][0] = F[y].b, e[cnte][1] = F[y].c, e[cnte][2] = y;
} else if (F[y].lb == x) {
F[y].lb = -1;
e[cnte][0] = F[y].a, e[cnte][1] = F[y].c, e[cnte][2] = y;
} else {
F[y].lc = -1;
e[cnte][0] = F[y].a, e[cnte][1] = F[y].b, e[cnte][2] = y;
}
}
if (F[x].lc == 0) {
cnte++;
e[cnte][0] = F[x].a, e[cnte][1] = F[x].b, e[cnte][2] = 0;
} else if (inCircumcircle(F[F[x].lc], A[insertNode]) == false) {
int y = F[x].lc;
cnte++;
if (F[y].la == x) {
F[y].la = -1;
e[cnte][0] = F[y].b, e[cnte][1] = F[y].c, e[cnte][2] = y;
} else if (F[y].lb == x) {
F[y].lb = -1;
e[cnte][0] = F[y].a, e[cnte][1] = F[y].c, e[cnte][2] = y;
} else {
F[y].lc = -1;
e[cnte][0] = F[y].a, e[cnte][1] = F[y].b, e[cnte][2] = y;
}
}
}
for (int i = 1; i <= cnte; ++i)
debug("e[%d] = (%d, %d, %d)\n", i, e[i][0], e[i][1], e[i][2]);
for (int i = 1; i <= cnte; ++i) {
Lk[e[i][0]].emplace_back(e[i][1], e[i][2]);
Lk[e[i][1]].emplace_back(e[i][0], e[i][2]);
}
NodeTp = 0;
dfs(e[1][0], 0, e[1][0]);
for (int i = 0; i < NodeTp; ++i)
debug("node = %d, to = %d, tof = %d\n", NodeStk[i], To[NodeStk[i]], Tof[NodeStk[i]]);
for (int i = 0; i < NodeTp; ++i) {
int id = CntF + (i % NodeTp) + 1;
F[id] = triangle_t(insertNode, NodeStk[i], To[NodeStk[i]], Tof[NodeStk[i]], CntF + ((i + 1) % NodeTp) + 1, CntF + ((i - 1 + NodeTp) % NodeTp) + 1);
if (Tof[NodeStk[i]] != 0) {
int y = Tof[NodeStk[i]];
if (F[y].la == -1 && cmp(NodeStk[i], To[NodeStk[i]], F[y].b, F[y].c)) F[y].la = id;
if (F[y].lb == -1 && cmp(NodeStk[i], To[NodeStk[i]], F[y].a, F[y].c)) F[y].lb = id;
if (F[y].lc == -1 && cmp(NodeStk[i], To[NodeStk[i]], F[y].a, F[y].b)) F[y].lc = id;
}
}
CntF += NodeTp;
for (int i = 0; i < NodeTp; ++i) Lk[NodeStk[i]].clear();
for (int i = 1; i <= CntF; ++i)
debug("i = %d, a = %d, b = %d, c = %d, la = %d, lb = %d, lc = %d, del = %d\n", i, F[i].a, F[i].b, F[i].c, F[i].la, F[i].lb, F[i].lc, Del[i]);
}
int CntE, Par[MaxN + 5];
edge_t ME[MaxF + 5];
int find(int x) { return x == Par[x] ? x : Par[x] = find(Par[x]); }
void solve() {
for (int i = 1; i <= N; ++i) insert(i);
for (int i = 1; i <= CntF; ++i)
if (Del[i] == false) debug("i = %d, a = %d, b = %d, c = %d, l = (%d, %d, %d)\n", i, F[i].a, F[i].b, F[i].c, F[i].la, F[i].lb, F[i].lc);
for (int i = 1; i <= N; ++i) Par[i] = i;
for (int i = 1; i <= CntF; ++i) {
if (F[i].a <= N && F[i].b <= N) ME[++CntE] = edge_t(F[i].a, F[i].b, mod(A[F[i].a] - A[F[i].b]));
if (F[i].a <= N && F[i].c <= N) ME[++CntE] = edge_t(F[i].a, F[i].c, mod(A[F[i].a] - A[F[i].c]));
if (F[i].b <= N && F[i].c <= N) ME[++CntE] = edge_t(F[i].b, F[i].c, mod(A[F[i].b] - A[F[i].c]));
}
std::sort(ME + 1, ME + 1 + CntE);
double ans = 0;
for (int i = 1; i <= CntE; ++i) {
int p = find(ME[i].u), q = find(ME[i].v);
if (p != q) {
Par[p] = q;
ans += ME[i].w;
}
}
printf("%.2lf\n", ans);
}
int main() {
#ifdef Tweetuzki
freopen("input.txt", "r", stdin);
freopen("output.txt", "w", stdout);
freopen("errorfile.txt", "w", stderr);
#endif
init();
solve();
return 0;
}
Guibas-Stolfi:
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
const int MaxN = 5000, MaxM = 100000;
const double eps = 1e-9;
#ifdef Tweetuzki
#define debug(arg...) fprintf(stderr, arg)
#else
#define debug(arg...) void(0)
#endif
typedef struct vec_t {
double x, y;
vec_t(double _x = 0, double _y = 0) { x = _x, y = _y; }
inline friend vec_t operator+(const vec_t &a, const vec_t &b) { return vec_t(a.x + b.x, a.y + b.y); }
inline friend vec_t operator-(const vec_t &a, const vec_t &b) { return vec_t(a.x - b.x, a.y - b.y); }
inline friend vec_t operator*(const vec_t &a, double k) { return vec_t(a.x * k, a.y * k); }
inline friend double dot(const vec_t &a, const vec_t &b) { return a.x * b.x + a.y * b.y; }
inline friend double cross(const vec_t &a, const vec_t &b) { return a.x * b.y - a.y * b.x; }
inline friend double mod(const vec_t &a) { return sqrt(a.x * a.x + a.y * a.y); }
inline void shake() {
if (rand() % 2 == 0) x += 1.0 * rand() / RAND_MAX * (1e-9);
else x -= 1.0 * rand() / RAND_MAX * (1e-9);
if (rand() % 2 == 0) y += 1.0 * rand() / RAND_MAX * (1e-9);
else y -= 1.0 * rand() / RAND_MAX * (1e-9);
}
} node_t;
typedef struct vec3_t {
double x, y, z;
vec3_t(double _x = 0, double _y = 0, double _z = 0) { x = _x, y = _y, z = _z; }
inline friend vec3_t operator+(const vec3_t &a, const vec3_t &b) { return vec3_t(a.x + b.x, a.y + b.y, a.z + b.z); }
inline friend vec3_t operator-(const vec3_t &a, const vec3_t &b) { return vec3_t(a.x - b.x, a.y - b.y, a.z - b.z); }
inline friend vec3_t cross(const vec3_t &a, const vec3_t &b) { return vec3_t(a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x); }
inline friend double dot(const vec3_t &a, const vec3_t &b) { return a.x * b.x + a.y * b.y + a.z * b.z; }
} node3_t;
struct Graph {
int cnte;
int head[MaxN + 5], to[MaxM * 2 + 5];
int pre[MaxM * 2 + 5], nxt[MaxM * 2 + 5];
Graph() {
cnte = 1;
memset(head, 0, sizeof head);
memset(to, 0, sizeof to);
memset(pre, 0, sizeof pre);
memset(nxt, 0, sizeof nxt);
}
inline void addEdge(int u, int v) {
cnte++;
to[cnte] = v;
nxt[cnte] = head[u];
pre[head[u]] = cnte;
head[u] = cnte;
}
inline void delEdge(int id) {
if (pre[id] != 0) nxt[pre[id]] = nxt[id];
if (nxt[id] != 0) pre[nxt[id]] = pre[id];
if (head[to[id ^ 1]] == id) head[to[id ^ 1]] = nxt[id];
}
};
struct edge_t {
int u, v;
double w;
edge_t(int _u = 0, int _v = 0, double _w = 0) { u = _u, v = _v, w = _w; }
inline friend bool operator<(const edge_t &a, const edge_t &b) { return a.w < b.w; }
};
int N, M;
node_t A[MaxN + 5], MemoryA[MaxN + 5];
int Indice[MaxN + 5];
int Par[MaxN + 5];
edge_t E[MaxM + 5];
Graph Gr;
inline node3_t mapping(const node_t &a) { return vec3_t(a.x, a.y, a.x * a.x + a.y * a.y); }
inline bool inCircumcircle(const node_t &a, const node_t &b, const node_t &c, const node_t &p) {
node3_t _a = mapping(a), _b = mapping(b), _c = mapping(c), _p = mapping(p);
if (cross(b - a, c - a) < 0) std::swap(_b, _c);
node3_t normal = cross(_b - _a, _c - _a);
if (dot(normal, _p - _a) > eps) return false;
else return true;
}
inline bool intersect(const node_t &a, const node_t &b, const node_t &c, const node_t &d) {
if (cross(c - a, b - a) * cross(b - a, d - a) < eps) return false;
if (cross(a - d, c - d) * cross(c - d, b - d) < eps) return false;
return true;
}
void init() {
srand(20040313U);
scanf("%d", &N);
for (int i = 1; i <= N; ++i) {
scanf("%lf %lf", &A[i].x, &A[i].y);
MemoryA[i] = A[i];
A[i].shake();
}
}
inline bool levelCompare(int x, int y) { return A[x].x < A[y].x; }
inline std::pair<int, int> getInitialBaseEdge(int l, int r) {
int m = (l + r) >> 1;
static int stk[MaxN + 5];
int tp = 0;
stk[++tp] = l, stk[++tp] = l + 1;
for (int i = l + 2; i <= r; ++i) {
while (tp > 1 && cross(A[Indice[stk[tp]]] - A[Indice[stk[tp - 1]]], A[Indice[i]] - A[Indice[stk[tp]]]) < eps) tp--;
stk[++tp] = i;
}
for (int i = 1; i < tp; ++i)
if (stk[i] <= m && stk[i + 1] > m) return std::make_pair(Indice[stk[i]], Indice[stk[i + 1]]);
return std::make_pair(-1, -1);
}
void fun(int l, int r) {
debug("fun(%d, %d)\n", l, r);
if (l == r) return;
if (l + 1 == r) {
Gr.addEdge(Indice[l], Indice[r]);
Gr.addEdge(Indice[r], Indice[l]);
return;
}
int m = (l + r) >> 1;
fun(l, m), fun(m + 1, r);
std::pair<int, int> base = getInitialBaseEdge(l, r);
for (;;) {
Gr.addEdge(base.first, base.second);
Gr.addEdge(base.second, base.first);
int leftFinal = -1, rightFinal = -1;
for (int i = Gr.head[base.first]; i; i = Gr.nxt[i]) {
int candidate = Gr.to[i];
if (cross(A[base.second] - A[base.first], A[candidate] - A[base.first]) < eps) continue;
if (leftFinal == -1 || inCircumcircle(A[base.first], A[base.second], A[leftFinal], A[candidate]) == true)
leftFinal = candidate;
}
for (int i = Gr.head[base.second]; i; i = Gr.nxt[i]) {
int candidate = Gr.to[i];
if (cross(A[candidate] - A[base.second], A[base.first] - A[base.second]) < eps) continue;
if (rightFinal == -1 || inCircumcircle(A[base.first], A[base.second], A[rightFinal], A[candidate]) == true)
rightFinal = candidate;
}
if (leftFinal == -1 && rightFinal == -1) break;
if (leftFinal != -1 && rightFinal != -1) {
if (inCircumcircle(A[base.first], A[base.second], A[leftFinal], A[rightFinal]) == true) leftFinal = -1;
else rightFinal = -1;
}
if (leftFinal != -1) {
for (int i = Gr.head[base.first]; i; i = Gr.nxt[i]) {
int linknode = Gr.to[i];
if (intersect(base.second, leftFinal, base.first, linknode) == true) {
Gr.delEdge(i);
Gr.delEdge(i ^ 1);
}
}
base = std::make_pair(leftFinal, base.second);
} else {
for (int i = Gr.head[base.second]; i; i = Gr.nxt[i]) {
int linknode = Gr.to[i];
if (intersect(base.first, rightFinal, base.second, linknode) == true) {
Gr.delEdge(i);
Gr.delEdge(i ^ 1);
}
}
base = std::make_pair(base.first, rightFinal);
}
}
}
int find(int x) { return x == Par[x] ? x : Par[x] = find(Par[x]); }
void solve() {
for (int i = 1; i <= N; ++i) Indice[i] = i;
std::sort(Indice + 1, Indice + 1 + N, levelCompare);
fun(1, N);
for (int u = 1; u <= N; ++u)
for (int i = Gr.head[u]; i; i = Gr.nxt[i]) {
int v = Gr.to[i];
if (u < v) {
debug("%d - %d\n", u, v);
E[++M] = edge_t(u, v, mod(MemoryA[v] - MemoryA[u]));
}
}
double ans = 0;
std::sort(E + 1, E + 1 + M);
for (int i = 1; i <= N; ++i) Par[i] = i;
for (int i = 1; i <= M; ++i) {
int u = E[i].u, v = E[i].v;
double w = E[i].w;
int p = find(u), q = find(v);
if (p != q) {
ans += w;
Par[p] = q;
}
}
printf("%.2lf\n", ans);
}
int main() {
#ifdef Tweetuzki
freopen("input.txt", "r", stdin);
freopen("output.txt", "w", stdout);
freopen("errorfile.txt", "w", stderr);
#endif
init();
solve();
return 0;
}