几何问题
几何相关算法
向量命名空间
用pt命令空间内的Point类,实现基本的向量加减乘除运算,大小比较<以及相等==判断,内积dot和外积cross,向量长度length,向量夹角angle,向量旋转rotate,以及一些求交点,判断是否正规相交,判断是否点在线段上,计算点到直线、线段距离的函数。
#include <cmath>
#include <string>
#include <cassert>
namespace pt {  // 创建pt向量命名空间
    const double EPS = 1e-10;
    const double PI = std::acos(-1);
    int sign(double x) { return std::fabs(x) < EPS ? 0 : (x < 0 ? -1 : 1); }
    struct Point {
        double x, y;
        Point(double x=0, double y=0):x(x), y(y) {}
        Point operator + (Point rhs) { return Point(x + rhs.x, y + rhs.y); }
        Point operator - (Point rhs) { return Point(x - rhs.x, y - rhs.y); }
        Point operator * (double rhs) { return Point(x * rhs, y * rhs); }
        Point operator / (double rhs) { assert(sign(rhs) != 0); return Point(x / rhs, y / rhs); }
        bool operator < (Point rhs) { return sign(x-rhs.x) == 0 ? sign(y-rhs.y) == -1 : x < rhs.x; }  // 可用于排序去重
        bool operator == (Point rhs) { return sign(x-rhs.x) == 0 && sign(y-rhs.y) == 0; }
        double dot(Point rhs) { return x * rhs.x + y * rhs.y; }
        double cross(Point rhs) { return x * rhs.y - y * rhs.x; }
        void print(std::string s = "") { printf("%s(%.2lf, %.2lf)", s.c_str(), x, y); }
    };
    double dot(Point A, Point B) { return A.x * B.x + A.y * B.y; }
    double cross(Point A, Point B) { return A.x * B.y - A.y * B.x; }
    double length(Point A) { return std::sqrt(dot(A, A)); }
    double angle(Point A, Point B) { return std::acos(dot(A, B) / length(A) / length(B)); }
    Point rotate(Point A, double rad) { return Point(A.x*std::cos(rad)-A.y*std::sin(rad), A.x*std::sin(rad)+A.y*std::cos(rad)); }
    Point line_intersection(Point A, Point u, Point B, Point v) {  // 求直线A+tu和B+tv的交点
        assert(sign(cross(u, v)) != 0);
        Point w = A - B;
        return A + u * cross(v, w) / cross(u, v);
    }
    bool segment_proper_intersection(Point A1, Point A2, Point B1, Point B2) {  // 判断线段A1A2是否与B1B2正规相交(不包含端点相交)
        double c1 = cross(A2-A1, B1-A1), c2 = cross(A2-A1, B2-A1);
        double c3 = cross(B2-B1, A1-B1), c4 = cross(B2-B1, A2-B1);
        return sign(c1*c2) == -1 && sign(c3*c4) == -1;
    }
    bool on_segment(Point P, Point A, Point B) {  // 判断P在线段AB内部(不包含端点)
        Point PA = A-P, PB = B-P;
        return sign(dot(PA, PB)) == -1 && sign(cross(PA, PB)) == 0;
    }
        double distance2line(Point P, Point A, Point B) { return std::fabs(cross(B-A, P-A)) / length(B-A); }  // 点P到直线AB的距离
    double distance2segment(Point P, Point A, Point B) {  // 点P到线段AB的距离
        if (A == B) return length(P-A);
        if (sign(dot(B-A, P-A)) == -1) return length(P-A);
        if (sign(dot(A-B, P-B)) == -1) return length(P-B);
        return distance2line(P, A, B);
    }
    double polygon_area(Point *P, int n) {  // 计算多边形的有向面积(逆时针为定义为正向)
        double area = 0;
        for (int i = 1; i < n-1; i++) area += cross(P[i+1]-P[0], P[i]-P[0]);
        return area / 2;
    }
    double convex_hull(Point *P, int n, Point *hull) {  // 计算大小为n的点集P的凸包hull,返回凸包大小
        std::sort(P, P+n); n = std::unique(P, P+n) - P;  // 优先对x排序,再对y排序,并去重
        int m = 0;
        for (int i = 0; i < n; i++) {  // 贪心求出下凸包
            while (m > 1 && sign(cross(hull[m-1]-hull[m-2], P[i]-hull[m-2])) <= 0) m--;
            hull[m++] = P[i];
        }
        int down = m;
        for (int i = n-2; i >= 0; i--) {  // 贪心求出上凸包,P[n-1]一定在下凸包中,不要重复枚举
            while (m > down && sign(cross(hull[m-1]-hull[m-2], P[i]-hull[m-2])) <= 0) m--;
            hull[m++] = P[i];
        }
        if (n > 1) m--; return m;  // 如果n>=2则P[0]在上下凸包中重复出现,将其舍去
    }
}
using namespace pt;常用拓扑定理
平面上的Euler定理
设图 的顶点数、区域数(包括外部区域)、边数分别为 ,记 的信息为 ,则有 。
思路:利用到 的生成树 ,和 的对偶图 和与 对应的 上的对偶生成树 ,利用生成树的性质证明该定理。主要需要证明对偶图的存在唯一性(同构意义下),以及 满足树的定义(没有环且链接全部的 中全部顶点)。
证明:首先给出对偶图的定义,若 的信息满足 ,则可通过以下方法构造对偶图 的构造,且 的顶点数为 :
- 将 的每个区域作为 中的顶点。
 - 对于 中的任意两个区域 ,如果 之间存在至少一个边,则在图 中连接 。
 
这样生成 的存在唯一性由上述构造方法可知。对于 中不属于 上的边 ,断言 一定划分了 的两个区域(反证法,利用 是树),也就是 在 中存在对应的边,全体满足此性质的边构成 。
下面证明 是树:
- 中无环,反证法,如果有环,则 一定不是树,因为由环围成的区域对应 中的点,该点一定不在 中,与 连接 中全部节点矛盾。
 - 连接 的全部节点,如果节点 不在 中,则说明在 中围成区域 的边都在 中,这与 无环矛盾。
 
综上,我们得到 是 中的树。记 为 中的边数,则由 的构造方法可知 ,由树的性质可知 ,结合上面两式可知 。
QED
下图中,黑色边为图 ,红色边为 和 ,铅笔给出了图 。

几何问题
      https://wty-yy.xyz/posts/54309/