二维凸包板子
\(Graham\) 求凸包:
找到 \(y\) 最小的点,将其他点按照极角排序。如果三点共线的话,优先连最远的点。每次比较栈顶两个点组成的向量,和栈顶点与新加入点组成的向量的叉积,只有叉积 \(>0\),说明在新向量在栈顶向量的逆时针方向时,才满足条件。
\(\text{Code}:\)
#include <bits/stdc++.h>
//#pragma GCC optimize(3)
#define int long long
#define ri register
#define mk make_pair
#define fi first
#define se second
#define pb push_back
#define eb emplace_back
#define is insert
#define es erase
using namespace std;
const int N = 200010;
inline int read()
{
int s = 0, w = 1;
ri char ch = getchar();
while (ch < '0' || ch > '9')
{
if (ch == '-')
w = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9')
s = (s << 3) + (s << 1) + (ch ^ 48), ch = getchar();
return s * w;
}
int n;
struct Node
{
double x, y;
} e[N];
int sta[N], tp;
inline double XJ(Node p1, Node q1, Node p2, Node q2)
{
return (q1.x - p1.x) * (q2.y - p2.y) - (q1.y - p1.y) * (q2.x - p2.x);
}
inline double Dis(Node x, Node y)
{
return sqrt((x.x - y.x) * (x.x - y.x) + (x.y - y.y) * (x.y - y.y));
}
inline bool cp(Node x, Node y)
{
double gt = XJ(e[1], x, e[1], y);
return (gt > 0 || (gt == 0 && Dis(e[1], x) < Dis(e[1], y)));
}
signed main()
{
n = read();
for (ri int i = 1; i <= n; i++)
{
scanf("%lf%lf", &e[i].x, &e[i].y);
if (e[i].y < e[1].y || (e[i].y == e[1].y && e[i].x < e[1].x))
swap(e[i], e[1]);
}
sort(e + 2, e + n + 1, cp);
sta[tp = 1] = 1;
for (ri int i = 2; i <= n; i++)
{
while (tp > 1 && XJ(e[sta[tp - 1]], e[sta[tp]], e[sta[tp]], e[i]) <= 0)
tp--;
sta[++tp] = i;
}
sta[++tp] = 1;
double res = 0;
for (ri int i = 1; i < tp; i++)
res += Dis(e[sta[i]], e[sta[i + 1]]);
printf("%.2lf\n", res);
return 0;
}
[SHOI2012]信用卡凸包
\(\text{Solution}:\)
对于每个信用卡,拿出四个圆心,然后对于这 \(4n\) 跑凸包,凸包周长加上一个半径为 \(R\) 的圆周长就是答案。可以用凸多边形内角和证明所有圆心角之和为 \(360°\)。
对于求信用卡旋转后四个圆心的坐标,设信用卡中心到每个圆心的距离为 \(L\),则右上方的圆心坐标为 \((Lcos(ori),Lsin(ori))\)(\(ori\) 指原始角大小),那么旋转 \(th\) 后,该坐标变为 \((Lcos(ori+th),Lsin(ori+th))\)。其他三个点的求法类似。
\(\text{Code}:\)
#include <bits/stdc++.h>
//#pragma GCC optimize(3)
#define int long long
#define ri register
#define mk make_pair
#define fi first
#define se second
#define pb push_back
#define eb emplace_back
#define is insert
#define es erase
using namespace std;
const int N = 100010;
inline int read()
{
int s = 0, w = 1;
ri char ch = getchar();
while (ch < '0' || ch > '9')
{
if (ch == '-')
w = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9')
s = (s << 3) + (s << 1) + (ch ^ 48), ch = getchar();
return s * w;
}
int n, m;
double A, B, R;
struct Node
{
double x, y;
} e[N];
int sta[N], tp;
inline double XJ(Node p1, Node q1, Node p2, Node q2)
{
return (q1.x - p1.x) * (q2.y - p2.y) - (q1.y - p1.y) * (q2.x - p2.x);
}
inline double Dis(Node x, Node y)
{
return sqrt((x.x - y.x) * (x.x - y.x) + (x.y - y.y) * (x.y - y.y));
}
inline bool cp(Node x, Node y)
{
double gt = XJ(e[1], x, e[1], y);
return (gt > 0 || (gt == 0 && Dis(e[1], x) < Dis(e[1], y)));
}
signed main()
{
n = read();
scanf("%lf%lf%lf", &A, &B, &R);
A -= 2.0 * R, B -= 2.0 * R;
double L = sqrt(A * A + B * B) * 0.5;
double ori = atan(A / B);
for (ri int i = 1; i <= n; i++)
{
double x, y, th;
scanf("%lf%lf%lf", &x, &y, &th);
e[++m] = (Node){x + L * cos(ori + th), y + L * sin(ori + th)};
e[++m] = (Node){x - L * cos(ori + th), y - L * sin(ori + th)};
e[++m] = (Node){x + L * cos(-ori + th), y + L * sin(-ori + th)};
e[++m] = (Node){x - L * cos(-ori + th), y - L * sin(-ori + th)};
}
for (ri int i = 1; i <= m; i++)
if (e[i].y < e[1].y || (e[i].y == e[1].y && e[i].x < e[1].x))
swap(e[i], e[1]);
sort(e + 2, e + 1 + m, cp);
sta[tp = 1] = 1;
for (ri int i = 2; i <= m; i++)
{
while (tp > 1 && XJ(e[sta[tp - 1]], e[sta[tp]], e[sta[tp]], e[i]) <= 0)
tp--;
sta[++tp] = i;
}
sta[++tp] = 1;
double res = acos(-1.0) * 2.0 * R;
for (ri int i = 1; i < tp; i++)
res += Dis(e[sta[i]], e[sta[i + 1]]);
printf("%.2lf\n", res);
return 0;
}
Wall
\(\text{Solution}:\)
显然,使得一些点离轮廓的距离等于 \(L\) 是最优的,不难发现这也是一个凸包。然后做法同信用卡凸包。
夜畔流离回,暗叹永无殿。
独隐万花翠,空寂亦难迁。
千秋孰能为,明灭常久见。
但得心未碎,踏遍九重天。