计算几何
计算几何
内容太多(105 页 ppt 呢
故只写大纲和之前不知道的东西
前置知识
向量
- 基本运算(加减、数乘、点乘、叉乘)
- 高维向量的运算
- 相关计算(长度、夹角、面积……
- 叉乘:
- 角度是有向的(从
转到 ) - 几何意义:平行四边形的有向面积
- 二维坐标计算:
- 角度是有向的(从
- n 维 n-1 个向量叉乘,结果是 n 维向量,系数是行列式
- 二维:
- 三维:方向是法向量,大小是平四面积
- 二维:
基于向量的表示法
- 点
- 直线
, 是任意一点, 是方向(一般用单位向量) - 线段
, 是一个端点, 是 到另一个端点的位移 - 多边形
,一般逆时针记录端点 - 圆
- 曲线:解析式
图形关系
点线
在直线上: 在线段上:在直线上 & 横纵坐标在区间内 在直线同侧:
线线
平行
: 与 重合:平行且 在 上
相交
- 快速排斥实验 & 跨立实验:判断线段是否有交
- 求直线的交点:列直线方程代入求解
点 & 多边形
- 先判去点在多边形边缘的情况
- 从该点引出任意一条射线,统计与多边形相交的次数。若奇数,则点在多边形内
- 对于射线经过多边形某边或某点的情况,规定射线上的点均在射线的同一侧
直线 & 圆
- 圆心到直线距离判位置关系
- 过圆心的垂线
,求交得到垂足 - 勾股算交点到垂足距离
圆圆
- 圆心距离判位置关系
- 交线方程
线圆交点
基本计算
- 点到直线距离
- 多边形面积:
,绝对值在求和号外面 - 旋转向量
极角排序
先判象限,同一象限按
int quadrant(const vector2d& u) { return (u.y < 0) << 1 | ((u.x < 0) ^ (u.y < 0)); } bool polar_cmp(const vector2d& u, const vector2d& v) { int qu = quadrant(u); int qv = quadrant(v); if (qu != qv) return qu < qv; ll d = det(u, v); if (d) return d > 0; return norm2(u) < norm2(v); }
二维凸包
Andrew 算法
- 所有点按 x 为第一关键字,y 为第二关键字排序
- 单调栈分别求上下凸壳
vector<poi> Andrew(vector<poi> p){ vector<poi> q;int tl=0; sort(p.begin(),p.end()); rep(i,0,p.size()-1){ while(tl>1 && cross(q[tl-2],q[tl-1],p[i])<=0) q.pop_back(),--tl; q.push_back(p[i]);++tl; } int tmp=tl; per(i,p.size()-2,0){ while(tl>tmp && cross(q[tl-2],q[tl-1],p[i])<=0) q.pop_back(),--tl; q.push_back(p[i]);++tl; } q.pop_back();return q; }
Graham 算法
- 钦定一个起点,所有点极角排序
- 同样是单调栈
- 一般不写
例题 [SHOI2012] 信用卡凸包
普通凸包板子 + 角上转一圈
闵可夫斯基和
另一类图形并(?
定义
从左下角开始,双指针扫,按照极角序依次加入每条边
vector<poi> minkowski(vector<poi> &a,vector<poi> &b){ vector<poi> c{a[0]+b[0]};int pa=1,pb=1; a.push_back(a[0]),b.push_back(b[0]); while(pa<(int)a.size() || pb<(int)b.size()){ if(pa<(int)a.size() && (pb==(int)b.size()|| sgn((a[pa]-a[pa-1])^(b[pb]-b[pb-1]))>=0)) c.push_back(c.back()+a[pa]-a[pa-1]),pa++; else c.push_back(c.back()+b[pb]-b[pb-1]),pb++; } return c; }
例题 [JSOI2018] 战争
题目要判断是否存在
移项得
于是判断
钦定起点,二分极角得到
旋转卡壳
在枚举凸包的边的同时,维护其他需要的点
可以在线性复杂度内解决凸包直径、最小矩形覆盖等问题
例题 [HNOI2007] 最小矩形覆盖
结论:最小矩形覆盖一定包含凸包上的一条边
枚举这条边,维护 3 个最远点即可
注意初始最左边的点要先赋值成初始最右边的点再扫,否则动不了
半平面
一条直线和直线的一侧,包含直线为闭,不包含为开
定义:
在计算几何中用向量表示,统一为向量的左侧
半平面交
- 加入边界(避免特判无穷大)
- 极角排序(斜率相同,靠左的在前面)+ 去重
- 单调队列维护(先弹队尾,再弹队首)
- 弹出条件(以队尾为例)
队尾的两直线交点在新直线右侧时,弹出队尾。 - 改进精度(代入交点坐标,解方程,去分母)
- 先弹队尾,再弹队首,全部结束后用队首弹队尾
- 弹出条件(以队尾为例)
- 判断无交
- 若最后只有
条线,一定无交 - 任取两条线的交点,判断它是否在所有射线的左边
- 特殊情况:存在平行且反向,互相在对方右侧的向量
如果出现,其他向量一定被弹没,只需判断队尾和前一个即可
- 若最后只有
- 求面积就是平凡的多边形
随机增量法
增量法:将一个问题化为刚好小一层的子问题
例题:Luogu 1742 最小圆覆盖
- 求三个点的外接圆:垂直平分线
- 增量构造
- 前 i-1 个点的圆是
,若 i 在 中,跳过 - 否则 i 一定在新的
上 - 重复找第一个不在圆
中的点,让圆 过这个点
- 前 i-1 个点的圆是
- 感性理解,最后一定能构造出前 i 个点的圆,因为不会出现两个点在 i “两端”的情况(否则 i 就在
中) - 复杂度分析:
- 由于最多只有 3 个点参与确定了最小覆盖圆,每个点参与的概率不大于
,也就是每层会循环到下一层的概率不大于 - 显然
,带有一个大概 13 的常数
- 由于最多只有 3 个点参与确定了最小覆盖圆,每个点参与的概率不大于
平面图转对偶
定义:原图每个面对应一个点,每条边左右的面连起来。
(虽然很丑但它是对偶图/kk
- 认为每个面是由逆时针的有向边拼起来的
- 原图中的边拆成两条有向边(对应两个面)
- 枚举每条边,若还没访问过,就从它出发逆时针转,找原图中它左边的面
- 对原图中每个点,从它出发的有向边极角排序
- 下一个要访问的边,就是当前边的反向边在极角序中的前驱
- 最后连接每条边对应的两个面
例题:Luogu 3249 矿区
先转对偶图,随便找一颗生成树,以无界域为根
非常厉害的结论:
多边形边界对应的树边,若内部是儿子,加上子树权值;否则,减去子树权值
画图可以感受到这个容斥是正确的,并适用于一切可加减的信息
习题
Luogu 3517 WYK-Plot
给定 n 个点,要求分成不超过 m 段,最小化每段最小覆盖圆的半径最大值。
二分答案,关键在于 check
我们不可以按顺序增量构造,因为随机增量复杂度才是对的。
从左到右构造,每个左端点找最大右端点,如果二分范围 [l,n],复杂度会悲催的成为
key: 倍增优化二分
考虑倍增段长找到第一个不符合要求的
用
加上二分答案的复杂度,最终复杂度是
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】