知识点 - 多边形合集

知识点 - 多边形合集

三角形有向面积

p 1 p 2 ⃗ \vec{p_1p_2} p1p2 p 2 p 3 ⃗ \vec{p_2p_3} p2p3 围成的平行四边形的面积为
2 S = ∣ x 2 − x 1 x 3 − x 2 y 2 − y 1 y 3 − y 2 ∣ = ( x 2 − x 1 ) ( y 3 − y 2 ) − ( x 3 − x 2 ) ( y 2 − y 1 ) 2S=\left|\begin{matrix}x_2-x_1 & x_3-x_2\\\\y_2-y_1 & y_3-y_2\end{matrix}\right|=(x_2-x_1)(y_3-y_2)-(x_3-x_2)(y_2-y_1) 2S=x2x1y2y1x3x2y3y2=(x2x1)(y3y2)(x3x2)(y2y1)
可以用它来判顺时针方向。

int signed_area_parallelogram(point2d p1, point2d p2, point2d p3) {
    return cross(p2 - p1, p3 - p2);
}

double triangle_area(point2d p1, point2d p2, point2d p3) {
    return abs(signed_area_parallelogram(p1, p2, p3)) / 2.0;
}

bool clockwise(point2d p1, point2d p2, point2d p3) {
    return signed_area_parallelogram(p1, p2, p3) < 0;
}

bool counter_clockwise(point2d p1, point2d p2, point2d p3) {
    return signed_area_parallelogram(p1, p2, p3) > 0;
}

多边形面积

直接积分:

A = ∑ ( p , q ) ∈ edges ( p x − q x ) ⋅ ( p y + q y ) 2 A = \sum_{(p,q)\in \text{edges}} \frac{(p_x - q_x) \cdot (p_y + q_y)}{2} A=(p,q)edges2(pxqx)(py+qy)

double area(const vector<point>& fig) {
    double res = 0;
    for (unsigned i = 0; i < fig.size(); i++) {
        point p = i ? fig[i - 1] : fig.back();
        point q = fig[i];
        res += (p.x - q.x) * (p.y + q.y);
    }
    return fabs(res) / 2;
}

也可以随意选一个点,然后把它和每条边组成的有向三角形求和。

凸包

可以极角排序,也可以xy坐标排序分别计算上下凸包。

xy排序

struct pt {
    double x, y;
};

bool cmp(pt a, pt b) {
    return a.x < b.x || (a.x == b.x && a.y < b.y);
}

bool cw(pt a, pt b, pt c) {
    return a.x*(b.y-c.y)+b.x*(c.y-a.y)+c.x*(a.y-b.y) < 0;
}

bool ccw(pt a, pt b, pt c) {
    return a.x*(b.y-c.y)+b.x*(c.y-a.y)+c.x*(a.y-b.y) > 0;
}

void convex_hull(vector<pt>& a) {
    if (a.size() == 1)
        return;

    sort(a.begin(), a.end(), &cmp);
    pt p1 = a[0], p2 = a.back();
    vector<pt> up, down;
    up.push_back(p1);
    down.push_back(p1);
    for (int i = 1; i < (int)a.size(); i++) {
        if (i == a.size() - 1 || cw(p1, a[i], p2)) {
            while (up.size() >= 2 && !cw(up[up.size()-2], up[up.size()-1], a[i]))
                up.pop_back();
            up.push_back(a[i]);
        }
        if (i == a.size() - 1 || ccw(p1, a[i], p2)) {
            while(down.size() >= 2 && !ccw(down[down.size()-2], down[down.size()-1], a[i]))
                down.pop_back();
            down.push_back(a[i]);
        }
    }

    a.clear();
    for (int i = 0; i < (int)up.size(); i++)
        a.push_back(up[i]);
    for (int i = down.size() - 2; i > 0; i--)
        a.push_back(down[i]);
}

极角排序

bool cmpr(V const &a, V const &b) {//cmpr必要时加上长度排序
	V v1 = a - st[0], v2 = b - st[0];
	return (abs(v1 | v2)<eps&&v1.L()<v2.L())||(v1 | v2) < -eps;
}
vector<V> ch;
void getCH() {
	sort(st + 1, st + n, cmpr);
	ch.push_back(st[0]);
	rep(i, 1, n-1) {
		while (ch.size() > 1 && (st[i] - ch.back() | ch.back() - ch[ch.size() - 2]) < eps)ch.pop_back();
		ch.push_back(st[i]);
	}
}
int out(int x) { return x ? x - 1 : ch.size() - 1; }
int in(int x) { return x + 1 == (int)ch.size() ? 0 : x + 1; }

多边形面积并

/*
简单多边形
顺时针存多边形
复杂度O(n^2m^2)
0 ≤ x, y ≤ 1 000
*/
#include
using namespace std;
#define mp make_pair
typedef long long ll;
const double inf=1e200;
const double eps=1e-12;
const double pi=4*atan(1.0);
int dcmp(double x){ return fabs(x)<eps?0:(x<0?-1:1);}
struct point{
    double x,y;
    point(double a=0,double b=0):x(a),y(b){}
};
point operator +(point A,point B) { return point(A.x+B.x,A.y+B.y);}
point operator -(point A,point B) { return point(A.x-B.x,A.y-B.y);}
point operator *(point A,double p){ return point(A.x*p,A.y*p);}
point operator /(point A,double p){ return point(A.x/p,A.y/p);}
bool operator ==(const point& a,const point& b){
    return fabs(a.x-b.x)<eps&&fabs(a.y-b.y)<eps;
}
double dot(point A,point B){ return A.x*B.x+A.y*B.y;}
double det(point A,point B){ return A.x*B.y-A.y*B.x;}
double det(point O,point A,point B){ return det(A-O,B-O);}
double length(point A){ return sqrt(dot(A,A));}
double area(vector<point>p){
    double ans=0; int sz=p.size();
    for(int i=1;i<sz-1;i++) ans+=det(p[i]-p[0],p[i+1]-p[0]);
    return ans/2.0;
}
double seg(point O,point A,point B){
    if(dcmp(B.x-A.x)==0) return (O.y-A.y)/(B.y-A.y);
    return (O.x-A.x)/(B.x-A.x);
}
vector<point>pp[110];
pair<double,int>s[110*60];
double polyunion(vector<point>*p,int N){
    double res=0;
    for(int i=0;i<N;i++){
        int sz=p[i].size();
        for(int j=0;j<sz;j++){
            int m=0;
            s[m++]=mp(0,0);
            s[m++]=mp(1,0);
            point a=p[i][j],b=p[i][(j+1)%sz];
            for(int k=0;k<N;k++){
                if(i!=k){
                    int sz2=p[k].size();
                    for(int ii=0;ii<sz2;ii++){
                        point c=p[k][ii],d=p[k][(ii+1)%sz2];
                        int c1=dcmp(det(b-a,c-a));
                        int c2=dcmp(det(b-a,d-a));
                        if(c1==0&&c2==0){
                            if(dcmp(dot(b-a,d-c))){
                                s[m++]=mp(seg(c,a,b),1);
                                s[m++]=mp(seg(c,a,b),-1);
                            }
                        }
                        else{
                            double s1=det(d-c,a-c);
                            double s2=det(d-c,b-c);
                            if(c1>=0&&c2<0) s[m++]=mp(s1/(s1-s2),1);
                            else if(c1<0&&c2>=0) s[m++]=mp(s1/(s1-s2),-1);
                        }
                    }
                }    
            }
            sort(s,s+m);
            double pre=min(max(s[0].first,0.0),1.0),now,sum=0;
            int cov=s[0].second;
            for(int j=1;j<m;j++){
                now=min(max(s[j].first,0.0),1.0);
                if(!cov) sum+=now-pre;
                cov+=s[j].second;
                pre=now;
            }
            res+=det(a,b)*sum;
        }
    }
    return res/2;
}
int main()
{
    int N,M,i,j; point tp;
    scanf("%d",&N);
    for(i=0;i<N;i++){
        scanf("%d",&M);
        for(j=0;j<M;j++){
            scanf("%lf%lf",&tp.x,&tp.y);
            pp[i].push_back(tp);
        }
    }
    double t1=0,t2=polyunion(pp,N);
    for(i=0;i<N;i++) t1+=area(pp[i]);
    printf("%.7lf %.7lf\n",-t1,-t2);
    return 0;
}

某点是否在凸多边形内 logn

(任意多边形的情况dls有讲过,就是画一条射线,看它穿过了几条变,要随机转)

先极角排序预处理每个点到极点的向量,然后可以 O ( l o g n ) O(logn) O(logn)询问。

二分极角找到最大的 i ,使得 p 0 , p i , p i + 1 p_0, p_i, p_{i + 1} p0,pi,pi+1组成的三角形可能包含该点。然后再真的判一下。

/*
	输入的是某点到ch[0]的向量。
	凸包是顺时针存的。
	要求凸包点不共线
*/
struct pt {
	long long x, y;
	pt(long long _x = 0, long long _y = 0) :x(_x), y(_y) {}
	pt operator+(const pt & p) const { return pt(x + p.x, y + p.y); }
	pt operator-(const pt & p) const { return pt(x - p.x, y - p.y); }
	bool operator<(const pt & p) const { return x < p.x || (x == p.x && y < p.y); }
	long long operator*(pt o) { return x * o.x + y * o.y; }
	long long operator|(pt o) { return x * o.y - o.x * y; }
	long long cross(const pt & p) const { return x * p.y - y * p.x; }
	long long dot(const pt & p) const { return x * p.x + y * p.y; }
	long long cross(const pt & a, const pt & b) const { return (a - *this).cross(b - *this); }
	long long dot(const pt & a, const pt & b) const { return (a - *this).dot(b - *this); }
	long long sqrLen() const { return this->dot(*this); }

};

pt L[N], S[N];
vector<pt> seq;
int n;
bool pointInTriangle(pt a, pt b, pt c, pt point) {
	long long s1 = abs(a.cross(b, c));
	long long s2 = abs(point.cross(a, b)) + abs(point.cross(b, c)) + abs(point.cross(c, a));
	return s1 == s2;
}
int sgn(long long val) {
	return val > 0 ? 1 : (val == 0 ? 0 : -1);
}
bool lexComp(const pt & l, const pt & r) {
	return l.x < r.x || (l.x == r.x && l.y < r.y);
}
void prepare(vector<pt> & points) {
	n = points.size();
	n--;
	seq.resize(n);
	for (int i = 0; i < n; i++)
		seq[i] = points[i + 1] - points[0];
	seq.push_back(seq.front());
}
bool pointInConvexPolygon(pt point) {
	if (seq[0].cross(point) != 0 && sgn(seq[0].cross(point)) != sgn(seq[0].cross(seq[n - 1])))
		return false;
	if (seq[n - 1].cross(point) != 0 && sgn(seq[n - 1].cross(point)) != sgn(seq[n - 1].cross(seq[0])))
		return false;

	if (seq[0].cross(point) == 0)
		return seq[0].sqrLen() >= point.sqrLen();

	int l = 0, r = n ;
	while (l <= r) {
		int mid = (l + r) / 2;
		int pos = mid;
		if (seq[pos].cross(point) >= 0)r = mid - 1;
		else l = mid + 1;
	}
	int pos = r;
	return pointInTriangle(pt(0,0),seq[pos], seq[pos + 1],  point);
}
vector<pt>  ch;
bool cmpr(pt const &a, pt const &b) {
	pt v1 = a - L[0], v2 = b - L[0];
	if ((v1 | v2) == 0)return v1.sqrLen() < v2.sqrLen();
	return (v1 | v2) < 0;
}

线段是否在多边形内

//预备函数
inline int opposite_side(point p1,point p2,point l1,point l2)
{
    return xmult(l1,p1,l2)*xmult(l1,p2,l2)<-eps;
}
inline int dot_online_in(point p,point l1,point l2)
{
    return zero(xmult(p,l1,l2))&&(l1.x-p.x)*(l2.x-p.x)<eps&&(l1.y-p.y)*(l2.y-p.y)<eps;
}

//判线段在任意多边形内,顶点按顺时针或逆时针给出,与边界相交返回1
int inside_polygon(point l1,point l2,int n,point* p)
{
    point t[MAXN],tt;
    int i,j,k=0;
    if (!inside_polygon(l1,n,p)||!inside_polygon(l2,n,p))
        return 0;
    for (i=0;i<n;i++)
    {
        if (opposite_side(l1,l2,p[i],p[(i+1)%n])&&opposite_side(p[i],p[(i+1)%n],l1,l2))
            return 0;
        else if (dot_online_in(l1,p[i],p[(i+1)%n]))
            t[k++]=l1;
        else if (dot_online_in(l2,p[i],p[(i+1)%n]))
            t[k++]=l2;
        else if (dot_online_in(p[i],l1,l2))
            t[k++]=p[i];
    }
    for (i=0;i<k;i++)
        for (j=i+1;j<k;j++)
        {
            tt.x=(t[i].x+t[j].x)/2;
            tt.y=(t[i].y+t[j].y)/2;
            if (!inside_polygon(tt,n,p))
                return 0;
        }
    return 1;
}

格点几何 :2017《正多边形》命题报告

格点多边形面积 pick公式

S = I + B 2 − 1 S=I+\frac{B}{2}-1 S=I+2B1

其中 I I I 是在多边形内的格点数, B B B 是格点边上的点数。

证明论文:http://www.geometer.org/mathcircles/pick.pdf

多边形内的格点数

#define abs(x) ((x)>0?(x):-(x))
struct point{int x,y;};

int gcd(int a,int b){
	return b?gcd(b,a%b):a;
}

//多边形上的网格点个数
int grid_onedge(int n,point* p){
	int i,ret=0;
	for (i=0;i<n;i++)
		ret+=gcd(abs(p[i].x-p[(i+1)%n].x),abs(p[i].y-p[(i+1)%n].y));
	return ret;
}

//多边形内的网格点个数
int grid_inside(int n,point* p){
	int i,ret=0;
	for (i=0;i<n;i++)
		ret+=p[(i+1)%n].y*(p[i].x-p[(i+2)%n].x);
	return (abs(ret)-grid_onedge(n,p))/2+1;
}

满足 0 ≤ x < n , 0 < y ≤ ⌊ k x + b ⌋ 0 \leq x < n,0 < y \leq \lfloor k x+b\rfloor 0x<n,0<ykx+b的格点数

int count_lattices(Fraction k, Fraction b, long long n) {
    auto fk = k.floor();
    auto fb = b.floor();
    auto cnt = 0LL;
    if (k >= 1 || b >= 1) {
        cnt += (fk * (n - 1) + 2 * fb) * n / 2;
        k -= fk;
        b -= fb;
    }
    auto t = k * n + b;
    auto ft = t.floor();
    if (ft >= 1) {
        cnt += count_lattices(1 / k, (t - t.floor()) / k, t.floor());
    }
        return cnt;
    }

exe:

Kattis - Convex Hull

Kattis - Keep the Parade Safe

Timus 1185: Wall

Usaco 2014 January Contest, Gold - Cow Curling

你可能感兴趣的:(数学-计算几何)