图片来源
3478 字
17 分钟
【更新中】AcWing 计算几何打卡记录
这个系列还没有完结,最近会不定时更新
基础知识
AcWing 2983. 玩具
严重怀疑数据有问题,暴力 WA,二分能过。
#include <iostream>#include <cstring>#define x first#define y second
using namespace std;
const int N = 5010;typedef long long LL;typedef pair<LL, LL> PLL;
PLL operator -(PLL a, PLL b) { return {a.x - b.x, a.y - b.y};}
LL operator *(PLL a, PLL b) { return a.x * b.y - a.y * b.x;}
LL area(PLL a, PLL b, PLL c) { return (b - a) * (c - a);}
struct Line { PLL u, d;} a[N];int res[N];
int main() { bool first = true; int n, m, x1, y1, x2, y2; while (scanf("%d", &n), n) { memset(res, 0, sizeof(res)); scanf("%d%d%d%d%d", &m, &x1, &y1, &x2, &y2); for (int i = 1; i <= n; ++i) { int u, l; scanf("%d%d", &u, &l); a[i] = {{u, y1}, {l, y2}}; } a[0] = {{x1, y1}, {x1, y2}}; a[n + 1] = {{x2, y1}, {x2, y2}}; for (int i = 1; i <= m; ++i) { PLL p; scanf("%lld%lld", &p.x, &p.y); int l = 0, r = n; while (l < r) { int mid = l + r + 1 >> 1; if (area(p, a[mid].u, a[mid].d) > 0) l = mid; else r = mid - 1; } res[l]++; } if (first) first = false; else printf("\n"); for (int i = 0; i <= n; ++i) { printf("%d: %d\n", i, res[i]); } } return 0;}AcWing 2984. 线段
#include <iostream>#include <cmath>#define x first#define y second
using namespace std;typedef pair<double, double> PDD;const int N = 110;const double eps = 1e-8;
struct Line { PDD s[2];} a[N];
int dcmp(double a, double b) { if (fabs(a - b) < eps) return 0; else if (a < b) return -1; else return 1;}
int sign(double a) { if (fabs(a) < eps) return 0; else if (a < 0) return -1; else return 1;}
PDD operator - (PDD a, PDD b) { return {a.x - b.x, a.y - b.y};}
double operator * (PDD a, PDD b) { return a.x * b.y - a.y * b.x;}
double area(PDD a, PDD b, PDD c) { return (b - a) * (c - a);}
void solve() { int n; scanf("%d", &n); for (int i = 1; i <= n; ++i) { scanf("%lf%lf%lf%lf", &a[i].s[0].x, &a[i].s[0].y, &a[i].s[1].x, &a[i].s[1].y); } for (int i = 1; i <= n; ++i) { for (int t1 = 0; t1 < 2; ++t1) { for (int j = 1; j <= n; ++j) { for (int t2 = 0; t2 < 2; ++t2) { PDD s = a[i].s[t1], t = a[j].s[t2]; if (!dcmp(s.x, t.x) && !dcmp(s.y, t.y)) continue; bool f = true; for (int k = 1; k <= n; ++k) { // cout << s.x << ' ' << s.y << ' ' << t.x << ' ' << t.y << ' ' << a[k].s[0].x << ' ' << a[k].s[0].y << ' ' << a[k].s[1].x << ' ' << a[k].s[1].y << endl; if (sign(area(s, t, a[k].s[0])) * sign(area(s, t, a[k].s[1])) > 0) { // cout << area(s, t, a[k].s[0]) << ' ' << area(s, t, a[k].s[1]) << endl; f = false; break; } } // return; if (f) { printf("Yes!\n"); return; } } } } } printf("No!\n");}int main() { int T; scanf("%d", &T); while (T--) { solve(); } return 0;}凸包
AcWing 1401. 围住奶牛
#include <iostream>#include <algorithm>#include <cmath>#define x first#define y secondusing namespace std;typedef pair<double, double> PDD;const int N = 10010;const double eps = 1e-8;PDD a[N];bool used[N];int st[N], siz;
PDD operator -(PDD a, PDD b) { return {a.x - b.x, a.y - b.y};}
double operator *(PDD a, PDD b) { return a.x * b.y - a.y * b.x;}
double area(PDD a, PDD b, PDD c) { return (b - a) * (c - a);}
int sign(double val) { if (fabs(val) < eps) return 0; else if (val < 0) return -1; else return 1;}
double len(PDD a) { return sqrt(a.x * a.x + a.y * a.y);}
int main() { int n; scanf("%d", &n); for (int i = 1; i <= n; ++i) { scanf("%lf%lf", &a[i].x, &a[i].y); } sort(a + 1, a + n + 1); for (int i = 1; i <= n; ++i) { while (siz >= 2 && sign(area(a[st[siz - 1]], a[st[siz]], a[i])) <= 0) { if (sign(area(a[st[siz - 1]], a[st[siz]], a[i])) == -1) used[st[siz]] = false; siz--; } st[++siz] = i; used[i] = true; } used[1] = false; for (int i = n - 1; i; --i) { if (used[i]) continue; while (siz >= 2 && sign(area(a[st[siz - 1]], a[st[siz]], a[i])) <= 0) siz--; st[++siz] = i; } cout << siz << endl; double res = 0; for (int i = 1; i < siz; ++i) { res += len(a[st[i]] - a[st[i + 1]]); } printf("%.2lf\n", res); return 0;}AcWing 2935. 信用卡凸包
- long double 要用
%Lf; - 括号套错,被洛谷卡了,这个问题是 GPT 发现的。
#include <iostream>#include <cmath>#include <algorithm>#define x first#define y secondusing namespace std;typedef long double LD;typedef pair<LD, LD> PDD;const int N = 40010;const LD eps = 1e-18;const LD PI = acosl(-1);
PDD q[N];int st[N], tp, m;bool used[N];
PDD operator +(const PDD& a, const PDD& b) { return {a.x + b.x, a.y + b.y};}
PDD operator -(const PDD& a, const PDD& b) { return {a.x - b.x, a.y - b.y};}
LD operator *(const PDD& a, const PDD& b) { return a.x * b.y - a.y * b.x;}
LD len(const PDD& a) { return sqrtl(a.x * a.x + a.y * a.y);}
PDD rotate(const PDD& a, LD t) { // 逆时针旋转 return {a.x * cosl(t) - a.y * sinl(t), a.x * sinl(t) + a.y * cosl(t)};}
LD area(const PDD& a, const PDD& b, PDD c) { return (b - a) * (c - a);}
int sign(LD a) { if (fabsl(a) < eps) return 0; else if (a < 0) return -1; else return 1;}
int main() { int n; LD a, b, r; scanf("%d", &n); scanf("%Lf%Lf%Lf", &a, &b, &r); PDD diag[2] = {{b / 2 - r, a / 2 - r}, {r - b / 2, a / 2 - r}}; for (int i = 1; i <= n; ++i) { LD px, py, t; scanf("%Lf%Lf%Lf", &px, &py, &t); q[++m] = PDD(px, py) + rotate(diag[0], t); q[++m] = PDD(px, py) + rotate(diag[1], t); q[++m] = PDD(px, py) - rotate(diag[0], t); q[++m] = PDD(px, py) - rotate(diag[1], t); } sort(q + 1, q + m + 1); for (int i = 1; i <= m; ++i) { while (tp >= 2 && sign(area(q[st[tp - 1]], q[st[tp]], q[i])) <= 0) { if (sign(area(q[st[tp - 1]], q[st[tp]], q[i])) == -1) used[st[tp--]] = false; else tp--; } st[++tp] = i; used[i] = true; } used[1] = false; for (int i = m - 1; i; --i) { if (used[i]) continue; while (tp >= 2 && sign(area(q[st[tp - 1]], q[st[tp]], q[i])) <= 0) tp--; st[++tp] = i; } LD res = 0; for (int i = 1; i < tp; ++i) { res += len(q[st[i + 1]] - q[st[i]]); } printf("%.2Lf\n", res + PI * r * 2); return 0;}半平面交
AcWing 2803. 凸多边形
没想到能出这么多事,被 hack 的两组边界数据如下图


- 交点求错,应该求前两条线的交点是否在这条线的右侧;
- 直接 dcmp 函数返回的 -1 和 1 当 sort 的 cmp,导致
nan和inf; - 多边形不相交输出
nan; - 交于一点输出
nan。
#include <iostream>#include <cmath>#include <algorithm>#define x first#define y secondusing namespace std;typedef pair<double, double> PDD;const int N = 510;const double eps = 1e-8;
struct Line { PDD s, t;} a[N];PDD p[60], ans[N];int q[N];
int dcmp(double a, double b) { if (fabs(a - b) < eps) return 0; else if (a < b) return -1; else return 1;}
int sign(double a) { if (fabs(a) < eps) return 0; else if (a < 0) return -1; else return 1;}
PDD operator -(PDD a, PDD b) { return {a.x - b.x, a.y - b.y};}
PDD operator +(PDD a, PDD b) { return {a.x + b.x, a.y + b.y};}
PDD operator *(PDD a, double t) { return {a.x * t, a.y * t};}
double operator *(PDD a, PDD b) { return a.x * b.y - a.y * b.x;}
double area(PDD a, PDD b, PDD c) { return (b - a) * (c - a);}
PDD get_line_intersection(PDD p, PDD v, PDD q, PDD w) { PDD u = p - q; double t = (w * u) / (v * w); return p + v * t;}
PDD get_line_intersection(Line a, Line b) { return get_line_intersection(a.s, a.t - a.s, b.s, b.t - b.s);}
double get_angle(Line a) { return atan2(a.t.y - a.s.y, a.t.x - a.s.x);}
bool cmp(Line a, Line b) { double A = get_angle(a), B = get_angle(b); return dcmp(A, B) ? A < B : area(a.s, a.t, b.s) < 0;}
bool on_right(Line a, Line b, Line c) { return sign(area(a.s, a.t, get_line_intersection(b, c))) <= 0;}
int main() { int n, m, l = 0; scanf("%d", &n); for (int i = 1; i <= n; ++i) { scanf("%d", &m); for (int j = 1; j <= m; ++j) { scanf("%lf%lf", &p[j].x, &p[j].y); } for (int j = 1; j < m; ++j) { a[++l] = {p[j], p[j + 1]}; } a[++l] = {p[m], p[1]}; } sort(a + 1, a + l + 1, cmp); int hh = 0, tt = -1; for (int i = 1; i <= l; ++i) { if (i > 1 && !dcmp(get_angle(a[i]), get_angle(a[i - 1]))) continue; while (hh + 1 <= tt && on_right(a[i], a[q[tt - 1]], a[q[tt]])) tt--; while (hh + 1 <= tt && on_right(a[i], a[q[hh]], a[q[hh + 1]])) hh++; q[++tt] = i; } while (hh + 1 <= tt && on_right(a[q[hh]], a[q[tt - 1]], a[q[tt]])) tt--; while (hh + 1 <= tt && on_right(a[q[tt]], a[q[hh]], a[q[hh + 1]])) hh++; q[++tt] = q[hh]; int len = 0; for (int i = hh; i < tt; ++i) { ans[++len] = get_line_intersection(a[q[i]], a[q[i + 1]]); // cout << q[i] << ' '; } // cout << endl; double res = 0; for (int i = 2; i < len; ++i) { res += area(ans[1], ans[i], ans[i + 1]); // cout << '(' << ans[i].x << ',' << ans[i].y << ')' << ' '; } // cout << endl; res = res / 2; printf("%.3lf\n", res); return 0;}AcWing 2957. 赛车
参考之前牛客多校的做法这道可以直接用单调栈做,因为只需要维护半个半平面交,斜率限定为正。
然后我又写挂了,a[i].k > a[st[tp]].k 写成了 a[i].k > a[i - 1].k,不知道我当时在想什么……
#include <iostream>#include <algorithm>#include <vector>
using namespace std;typedef long long LL;const int N = 10010;struct Node{ LL k, v; int id;} a[N];int st[N], ans[N], tp;vector<int> t[N];
int main() { int n; scanf("%d", &n); for (int i = 1; i <= n; ++i) { scanf("%lld", &a[i].k); a[i].id = i; } for (int i = 1; i <= n; ++i) { scanf("%lld", &a[i].v); } sort(a + 1, a + n + 1, [](Node a, Node b) { return a.v == b.v ? a.k < b.k : a.v < b.v; }); for (int i = 1; i <= n; ++i) { while (tp > 1 && (a[i].k - a[st[tp]].k) * (a[st[tp - 1]].v - a[st[tp]].v) < (a[st[tp]].k - a[st[tp - 1]].k) * (a[st[tp]].v - a[i].v) || tp && a[i].k > a[st[tp]].k) tp--; st[++tp] = i; } for (int i = 1; i <= tp; ++i) { ans[i] = a[st[i]].id; } sort(ans + 1, ans + tp + 1); printf("%d\n", tp); for (int i = 1; i <= tp; ++i) { if (i == 1) printf("%d", ans[i]); else printf(" %d", ans[i]); } printf("\n"); return 0;}旋转卡壳
AcWing 2119. 最佳包裹
我这还没几天又和之前凸包挂在了同一个地方,used[st[tp]] = false 而不是 used[tp] = false.
成惯犯了……
#include <iostream>#include <algorithm>#include <cmath>#define x first#define y secondusing namespace std;typedef long long LL;typedef pair<LL, LL> PLL;const int N = 50010;PLL a[N];int st[N], tp;bool used[N];
PLL operator -(PLL a, PLL b) { return {a.x - b.x, a.y - b.y};}
LL operator *(PLL a, PLL b) { return a.x * b.y - a.y * b.x;}
LL area(PLL a, PLL b, PLL c) { return (b - a) * (c - a);}
LL get_dis(PLL a, PLL b) { return (a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y);}
int main() { int n; scanf("%d", &n); for (int i = 1; i <= n; ++i) { scanf("%lld%lld", &a[i].x, &a[i].y); } sort(a + 1, a + n + 1); for (int i = 1; i <= n; ++i) { while (tp > 1 && area(a[st[tp - 1]], a[st[tp]], a[i]) <= 0) { if (area(a[st[tp - 1]], a[st[tp]], a[i]) < 0) used[st[tp]] = false; tp--; } st[++tp] = i; used[i] = true; } used[1] = false; for (int i = n - 1; i; --i) { if (used[i]) continue; while (tp > 1 && area(a[st[tp - 1]], a[st[tp]], a[i]) <= 0) tp--; st[++tp] = i; } tp--; if (tp <= 2) { printf("%lld\n", get_dis(a[1], a[n])); } else { LL res = 0; for (int i = 1, j = 3; i < tp; ++i) { while (area(a[st[i]], a[st[i + 1]], a[st[j]]) <= area(a[st[i]], a[st[i + 1]], a[st[j % (tp - 1) + 1]])) j = j % (tp - 1) + 1; res = max(res, max(get_dis(a[st[i]], a[st[j]]), get_dis(a[st[i + 1]], a[st[j]]))); } printf("%lld\n", res); } return 0;}AcWing 2142. 最小矩形覆盖
我又又又又把凸包写挂了,这次更严重,不但写出了个 used[i] = false,而且 Andrew 第二轮的时候没判断有没有被取过。
#include <iostream>#include <cmath>#include <algorithm>#define x first#define y second
using namespace std;
typedef pair<double, double> PDD;const int N = 50010;const double eps = 1e-12;const double PI = acos(-1);
PDD a[N];int st[N], tp;bool used[N];
int sign(double a) { if (fabs(a) < eps) return 0; else if (a < 0) return -1; else return 1;}
int dcmp(double a, double b) { if (fabs(a - b) < eps) return 0; else if (a < b) return -1; else return 1;}
PDD operator +(PDD a, PDD b) { return {a.x + b.x, a.y + b.y};}
PDD operator -(PDD a, PDD b) { return {a.x - b.x, a.y - b.y};}
PDD operator *(PDD a, double b) { return {a.x * b, a.y * b};}
PDD operator /(PDD a, double b) { return {a.x / b, a.y / b};}
double operator *(PDD a, PDD b) { return a.x * b.y - a.y * b.x;}
double operator &(PDD a, PDD b) { return a.x * b.x + a.y * b.y;}
double area(PDD a, PDD b, PDD c) { return (b - a) * (c - a);}
double getlen(PDD a) { return sqrt(pow(a.x, 2) + pow(a.y, 2));}
double proj(PDD a, PDD b, PDD c) { // proj_(b - a) (c - b) return ((b - a) & (c - b)) / getlen(b - a);}
PDD rotate(PDD a, double t) { return {a.x * cos(t) - a.y * sin(t), a.x * sin(t) + a.y * cos(t)};}
PDD unit(PDD a) { return a / getlen(a);}
ostream &operator <<(ostream &cout, const PDD &a) { cout << '(' << a.x << ',' << a.y << ')'; return cout;}
int main() { int n; scanf("%d", &n); for (int i = 1; i <= n; ++i) { scanf("%lf%lf", &a[i].x, &a[i].y); } sort(a + 1, a + n + 1); for (int i = 1; i <= n; ++i) { while (tp > 1 && sign(area(a[st[tp - 1]], a[st[tp]], a[i])) <= 0) { if (sign(area(a[st[tp - 1]], a[st[tp]], a[i])) == -1) used[st[tp]] = false; tp--; } st[++tp] = i; used[i] = true; } used[1] = false; for (int i = n; i; --i) { if (used[i]) continue; while (tp > 1 && sign(area(a[st[tp - 1]], a[st[tp]], a[i])) <= 0) tp--; st[++tp] = i; } tp--; double res = 1e18; PDD p[4]; // for (int i = 1; i <= tp; ++i) cout << a[st[i]] << ' '; // cout << endl; for (int i = 1, j = 3, k = 2, l = 3; i < tp; ++i) { PDD &c = a[st[i]], &d = a[st[i + 1]]; while (dcmp(area(c, d, a[st[j]]), area(c, d, a[st[j % tp + 1]])) < 0) j = j % tp + 1; while (dcmp(proj(c, d, a[st[k]]), proj(c, d, a[st[k % tp + 1]])) < 0) k = k % tp + 1; if (i == 1) l = j; while (dcmp(proj(c, d, a[st[l]]), proj(c, d, a[st[l % tp + 1]])) > 0) l = l % tp + 1; PDD &e = a[st[k]], &f = a[st[j]], &g = a[st[l]]; double h = area(c, d, f) / getlen(d - c), w = ((e - g) & (d - c)) / getlen(d - c); if (dcmp(res, h * w) > 0) { res = h * w; PDD dh = unit(rotate(d - c, PI / 2)) * h; p[0] = d + unit(d - c) * proj(c, d, g); p[1] = d + unit(d - c) * proj(c, d, e); p[2] = p[1] + dh; p[3] = p[0] + dh; } } printf("%.5lf\n", res); int fir = 0; for (int i = 0; i < 4; ++i) { // -0.00000 不要背刺我😭 if (sign(p[i].x) == 0) p[i].x = 0; if (sign(p[i].y) == 0) p[i].y = 0; if (dcmp(p[fir].y, p[i].y) == 1) fir = i; else if (dcmp(p[fir].y, p[i].y) == 0 && dcmp(p[fir].x, p[i].x) == 1) fir = i; } for (int i = 0; i < 4; ++i) { printf("%.5lf %.5lf\n", p[(i + fir) % 4].x, p[(i + fir) % 4].y); } return 0;}扫描线
AcWing 3068. 扫描线
又是熟悉的低级错误时间。
- 区间合并跳到下一个区间的时候没初始化左右端点 * 1;
- 有效区间长度为 0 没有特判,越界访问了上一次的第一个区间 * 1。
#include <iostream>#include <algorithm>#include <vector>#define x first#define y second
using namespace std;typedef long long LL;typedef pair<int, int> PII;const int N = 1010;PII a[N], b[N], t[N];vector<int> values;int n;
LL work(int l, int r) { int len = 0; for (int i = 1; i <= n; ++i) { if (a[i].x <= l && b[i].x >= r) { t[++len] = {a[i].y, b[i].y}; } } if (!len) return 0; // 空的 sort(t + 1, t + len + 1); LL res = 0; int L = t[1].x, R = t[1].y; for (int i = 2; i <= len; ++i) { if (t[i].x <= R) { R = max(R, t[i].y); } else { res += R - L; L = t[i].x, R = t[i].y; } } res += R - L; return res * (r - l);}
int main() { scanf("%d", &n); for (int i = 1; i <= n; ++i) { scanf("%d%d%d%d", &a[i].x, &a[i].y, &b[i].x, &b[i].y); values.emplace_back(a[i].x), values.emplace_back(b[i].x); } sort(values.begin(), values.end()); int m = values.size() - 1; LL res = 0; for (int i = 0; i < m; ++i) { if (values[i] != values[i + 1]) res += work(values[i], values[i + 1]); } printf("%lld\n", res); return 0;} 【更新中】AcWing 计算几何打卡记录
https://starlab.top/posts/geometry-acwing/ 部分信息可能已经过时







