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=∣∣∣∣∣∣x2−x1y2−y1x3−x2y3−y2∣∣∣∣∣∣=(x2−x1)(y3−y2)−(x3−x2)(y2−y1)
可以用它来判顺时针方向。
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(px−qx)⋅(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;
}
(任意多边形的情况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《正多边形》命题报告
S = I + B 2 − 1 S=I+\frac{B}{2}-1 S=I+2B−1
其中 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 0≤x<n,0<y≤⌊kx+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