基本操作
判断两线段是否相交
跨立实验,两点是否在异侧,两条线段都成立即相交。
此实现不含端点
bool Cross(node A,node B,node C,node D){return ((B-A)*(C-A))*((B-A)*(D-A))<0&&((D-C)*(A-C))*((D-C)*(B-C))<0;
}
判断点是否在直线上
判外积是否为零可知是否共直线,需两点异侧,判内积是否为非正即可。
此实现含端点
bool inedge(node A,node B,node x){return (A-x)*(B-x)==0&&((A-x)^(B-x))<=0;
}
判断一点是否在多边形内部
射线法,从该点引一条水平射线,判断此线经过多边形的边次数奇偶性即可。
考虑边界问题,单独处理该点在多边形边上的情况。
经过多边形顶点的情况,只计算在其上方的边的个数。
bool inpolygon(node x){int tot=0;rpt(i,1,n)if(inedge(i==1?a[n]:a[i-1],a[i],x))return 1;rpt(i,1,n)tot+=Cross(i==1?a[n]:a[i-1],a[i],x,{inf,x.y});rpt(i,1,n)if(x.y-a[i].y==0&&a[i].x>x.x)tot+=((i==1?a[n].y:a[i-1].y)>x.y)+((i==n?a[1].y:a[i+1].y)>x.y);return tot%2;
}
例题
Security at Museums
简要题意:
\(n\) 个顶点的平面多边形,集合为凸包的点集计数。
\(sol\):
先预处理出任意两点间线段是否在多边形内。
若中间经过端点则为合并两个线段,记忆化后可做到 \(O(n^3)\)。
若中间经过多边形边(不含端点)则为假。
否则判中点是否在多边形内部即可。
然后考虑 \(dp\)。
先钦定 \(dp\) 顺序,考虑一个凸包的极角一定是单调的,先按将线段按极角排序。
考虑极角相同的线段的顺序是一定的,将极角相同的线段一起转移。
一条线段中间经过的端点可选可不选,贡献为 \(2^n\)。
一个凸包的贡献为所有线段贡献的乘积。
最后减去只有两个点的多余贡献即可。