luuia
2024-09-14 16:23:35
在同一个平面上互相垂直且有公共原点的两条数轴构成平面直角坐标系,简称直角坐标系(Rectangular Coordinates)。通常,两条数轴分别置于水平位置与垂直位置,取向右与向上的方向分别为两条数轴的正方向。
在平面直角坐标系下,点采用坐标形式表示。我们可以使用 pair
或者结构体来存储。
在二维计算几何中,为了避免直线多种表达式的转换,我们先选定直线上的一个点,用一个向量表示它的倾斜程度,只需要记录这个点和方向向量即可。线段只需要记录两端端点的坐标即可。
向量在平面直角坐标系下表示为一条有向线段
显然这样定义后的向量与坐标是一一对应的,所以只需要像点一样存向量即可。
开一个数组,按一定的顺序记录顶点的坐标。圆记录圆心和半径即可。
#define Vector Node
const int N = 1e2 + 10;
const double eps = 1e-8,pi = 3.14159265358979;
int dcmp(double x){return x > eps ? 1 : x < -eps ? -1 : 0;} // 正负号
double dabs(double x){return x * dcmp(x);} // 绝对值
double S(double x){return x * x;} // 平方
struct Node // 点
{
double x,y;
Node(double X = 0,double Y = 0){x = X,y = Y;}
void read(){cin >> x >> y;} // 读入
void write(){cout << fixed << setprecision(2) << x << " " << y << endl;} // 输出
};
struct Line{Node A,B;};// 直线
struct Circle
{
Node O;double r;
Circle(Node P,double R = 0){O = P,r = R;}
};
struct Polygon
{
Node P[N];int n;
void read(){cin >> n;for(int i = 1;i <= n;i++) P[i].read();}
void write(){for(int i = 1;i <= n;i++) P[i].write();}
};// 多边形
设三角形
多边形面积公式:记多边形的边数为
上述公式证明不难。
对于
对于
两个向量点乘的结果是一个数值。
对于
其中
对于
夹角
两个向量叉乘的结果还是一个数值。
对于
向量位置与向量叉积的关系:
向量
(1) 对于点
记向量
旋转后
(2) 对于点
将
(3) 对于点
先将坐标系原点平移到
(4) 对于点
将
Node operator + (Node a,Node b){return Node(a.x + b.x,a.y + b.y);} // 加
Node operator - (Node a,Node b){return Node(a.x - b.x,a.y - b.y);} // 减
Node operator * (Node a,double b){return Node(a.x * b,a.y * b);} // 数乘
Node operator / (Node a,double b){return Node(a.x / b,a.y / b);} // 除法
Node operator - (Node a){return Node(-a.x,-a.y);}
bool operator == (Node a,Node b){return !dcmp(a.x - b.x) && !dcmp(a.y - b.y);} // 点相等
double operator ^ (Vector a,Vector b){return a.x * b.x + a.y * b.y;} // 点乘
double operator * (Vector a,Vector b){return a.x * b.y - a.y * b.x;} // 叉乘
double len(Node a){return sqrt(a ^ a);} // 到原点的距离/模长
double sqr_len(Node a){return a ^ a;} // 到原点的距离平方/模长平方
Node Normal(Node a){return Node(-a.y,a.x);} // 法向量
double slope(Line l){return atan2(l.B.y - l.A.y,l.B.x - l.A.x);}
bool operator == (Line l,Line r){return (l.A == r.A && l.B == r.B) || (l.B == r.A && l.A == r.B);} // 直线相等
bool operator < (Line l,Line r){return dcmp(slope(l) - slope(r)) ? dcmp(slope(l) - slope(r)) < 0 : dcmp((r.B - r.A) * (l.A - r.A)) > 0;}
bool Node_on_Line(Node p,Line l){return !dcmp((p - l.A) * (l.B - l.A));} // 点在直线上
bool Node_on_Seg(Node p,Line l){return !dcmp((p - l.A) * (l.B - l.A)) && dcmp((p - l.A) ^ (p - l.B)) <= 0;} // 点在线段上
double Dis_Node_Node(Node a,Node b){return sqrt(S(a.x - b.x) + S(a.y - b.y));} // 点到点距离
double Dis_Node_Line(Node p,Line l) // 点到直线距离
{
Node a = l.A,b = l.B;
if(a == b) return len(p - a);
Vector x = Vector(p.x - a.x,p.y - a.y),y = Vector(p.x - b.x,p.y - b.y),z = Vector(b.x - a.x,b.y - a.y);
if(dcmp(x ^ z) < 0) return len(x);
if(dcmp(y ^ z) > 0) return len(y);
return dabs(x * z / len(z));// 面积除以底边长
}
double Seg_Len(Line l){return Dis_Node_Node(l.A,l.B);} // 线段长度
Node turn(Node a,Node b,double theta) // 点旋转
{
double x = (a.x - b.x) * cos(theta) + (a.y - b.y) * sin(theta) + b.x;
double y = -(a.x - b.x) * sin(theta) + (a.y - b.y) * cos(theta) + b.y;
return Node(x,y);
}
(1) 点
我们只需要判断
bool Node_on_Line(Node p,Line l){return !dcmp((p - l.A) * (l.B - l.A));} // 点在直线上
(2) 点
判断
bool Node_on_Seg(Node p,Line l){return !dcmp((p - l.A) * (l.B - l.A)) && dcmp((p - l.A) ^ (p - l.B)) <= 0;} // 点在线段上
(3) 点
若
若
double Dis_Node_Line(Node p,Line l) // 点到直线距离
{
Node a = l.A,b = l.B;
if(a == b) return len(p - a);
Vector x = p - a,y = p - b,z = b - a;
if(dcmp(x ^ z) < 0) return len(x);
if(dcmp(y ^ z) > 0) return len(y);
return dabs(x * z / len(z)); // 面积除以底边长
}
(4) 作点
求出
Node FootPoint(Node p,Line l) // 点到直线垂足
{
Node a = l.A,b = l.B;
Vector x = p - a,y = p - b,z = b - a;
double len1 = x ^ z / len(z),len2 = -1.0 * (y ^ z) / len(z);
return a + z * (len1 / (len1 + len2));
}
(5) 作点
作出垂足
Node Symmetry_Node_Line(Node p,Line l) {return p + (FootPoint(p,l.A,l.B) - p) * 2;} // 作一个点关于直线的对称点
(1) 直线
计算出
Node Line_Line_Intersection(Line l,Line r)
{
Vector x = l.B - l.A,y = r.B - r.A,z = l.A - r.A;
return l.A + x * ((y * z) / (x * y));
}
(2) 直线
判断直线
bool Line_Cross_Seg(Line l,Line r){return Node_on_Seg(Line_Line_Intersection(l,r),r);}
(3) 线段
判断直线
bool Seg_Cross_Seg(Line l,Line r){return Node_on_Seg(Line_Line_Intersection(l,r),l) && Node_on_Seg(Line_Line_Intersection(l,r),r);}
(1) 判断点
考虑以
int Node_in_Polygon(Polygon X,Node a) // 点在任意多边形以内
{
int cnt = 0,n = X.n;
double tmp;
for(int i = 1;i <= n;i++)
{
int j = i % n + 1;
if(Node_on_Seg(a,(Line){X.P[i],X.P[j]})) return 2; // 点在多边形上
if(a.y >= min(X.P[i].y,X.P[j].y) && a.y < max(X.P[i].y,X.P[j].y)) tmp = X.P[i].x + (a.y - X.P[i].y) / (X.P[j].y - X.P[i].y) * (X.P[j].x - X.P[i].x),cnt += dcmp(tmp - a.x) > 0;
}
return cnt & 1;
}
(2) 判断线段
int Line_in_Polygon(Polygon X,Line l) // 线段在任意多边形以内
{
if(Node_in_Polygon(X,l.A) == 0 || Node_in_Polygon(X,l.B) == 0) return 0;
int n = X.n;
for(int i = 1;i <= n;i++)
{
int j = i % n + 1;
if(Seg_Cross_Seg(l,(Line){X.P[i],X.P[j]})) return 0;
if(!dcmp(Dis_Node_Line(l.A,(Line){X.P[i],X.P[j]})) && !dcmp(Dis_Node_Line(l.B,(Line){X.P[i],X.P[j]}))) return 2; // 线段在多边形上
}
return 1;
}
(3) 判断线段
int Line_in_ConvexPolygon(Polygon X,Line l){return Node_in_Polygon(X,l.A) && Node_in_Polygon(X,l.B);} // 线段在凸多边形以内
判断任意两个多边形是否相离:属于不同多边形的任意两边都不相交且一个多边形上的任意点都不被另一个多边形所包含。
bool check_polygon(Polygon X,Polygon Y) // 两个多边形是否相离
{
int n = X.n,m = Y.n;
for(int i = 1;i <= n;i++)
{
for(int j = 1;j <= m;j++)
{
if(Seg_Cross_Seg((Line){X.P[i],X.P[i % n + 1]},(Line){Y.P[j],Y.P[j % m + 1]})) return 0;
if(Node_in_Polygon(Y,X.P[i]) || Node_in_Polygon(X,Y.P[j])) return 0;
}
}
return 1;
}
在平面直角坐标系中,设点
点
类似的,在
在平面直角坐标系中,设点
类似的,在
曼哈顿距离有性质:
在平面直角坐标系中,设点
类似的,在
设
这就是
类似的,将每一个点
非常神奇的一个距离,它在不同的情况下表示了欧几里得距离,曼哈顿距离和切比雪夫距离。
在
当
当
给定三点
代入解方程能得到:
Circle getCircle(Node A,Node B,Node C)
{
double x1 = A.x,y1 = A.y,x2 = B.x,y2 = B.y,x3 = C.x,y3 = C.y;
double D = ((S(x2) + S(y2) - S(x3) - S(y3)) * (y1 - y2) - (S(x1) + S(y1) - S(x2) - S(y2)) * (y2 - y3)) / ((x1 - x2) * (y2 - y3) - (x2 - x3) * (y1 - y2));
double E = (S(x1) + S(y1) - S(x2) - S(y2) + D * (x1 - x2)) / (y2 - y1);
double F = -(S(x1) + S(y1) + D * x1 + E * y1);
return Circle(Node(-D / 2.0,-E / 2.0),sqrt((S(D) + S(E) - 4.0 * F) / 4.0));
}
Circle getcircle(Node A,Node B,Node C)
{
Node P1 = (A + B) / 2,P2 = (A + C) / 2;
Node O = Line_Line_Intersection((Line){P1,P1 + Normal(B - A)},(Line){P2,P2 + Normal(C - A)});
return Circle(O,len(A - O));
}
首先引入一个定理:若
我们将点集
于是我们从小到大枚举
void Random(Node P[],int n){for(int i = 1;i <= n;i++) swap(P[i],P[rand() % n + 1]);}
Circle Min_Circle_Cover(Node *P,int n)
{
Random(P,n);
Circle C = Circle(P[1],0);
for(int i = 2;i <= n;i++)
{
if(Node_in_Circle(C,P[i]) == 0)
{
C = Circle(P[i],0);
for(int j = 1;j < i;j++)
{
if(Node_in_Circle(C,P[j]) == 0)
{
C.O = (P[i] + P[j]) / 2,C.r = len(P[j] - C.O);
for(int k = 1;k < j;k++) if(Node_in_Circle(C,P[k])) C = getCircle(P[i],P[j],P[k]);
}
}
}
}
return C;
}
定义凸多边形是一个所有内角都在
将所有点按照
我们首先让
最后将最小的元素与栈顶比较,保证最后一段也是凸壳。
可以发现,这样能够求出下凸壳,将顶点倒序扫一遍即可求出上凸壳。
int ConvexHull(Polygon X,Node *ch)
{
int n = X.n,t = 0;
sort(X.P + 1,X.P + n + 1,cmp_Node);
for(int i = 1;i <= n;i++) // 下凸包
{
while(t > 1 && dcmp((ch[t] - ch[t - 1]) * (X.P[i] - ch[t - 1])) <= 0) --t;
ch[++t] = X.P[i];
}
int St = t;
for(int i = n - 1;i >= 1;i--) // 上凸包
{
while(t > St && dcmp((ch[t] - ch[t - 1]) * (X.P[i] - ch[t - 1])) <= 0) --t;
ch[++t] = X.P[i];
}
return --t; // 减一因为最后的 P1 被统计了两遍
}
它通过依次枚举凸包上某一条边的同时,维护其他需要的点,能够线性求解凸包直径等和凸包性质相关的问题。
(1) 给定平面上
先求出点集的凸包,然后我们考虑选定凸包的一条边所在的直线,比如
我们发现枚举时最远点也是逆时针旋转的。换句话说,逆时针遍历的点到直线的距离单调。证明可以用凸包的凸性来解释。这意味着我们可以在逆时针枚举凸包上的边时,维护一个当前最远点更新答案。
对于每条边,考虑
double Convex_Diameter(Polygon X)
{
int n = X.n;
double res = len(X.P[2] - X.P[1]);
if(n <= 2){return res;}
if(n == 3){return max(len(X.P[2] - X.P[1]),max(len(X.P[3] - X.P[2]),len(X.P[3] - X.P[1])));}
for(int i = 1,j = 3;i <= n;i++)
{
while(dcmp((X.P[i + 1] - X.P[i]) * (X.P[j] - X.P[i]) - (X.P[i + 1] - X.P[i]) * (X.P[j + 1] - X.P[i])) < 0) j = j % n + 1;
res = max(res,max(len(X.P[j] - X.P[i]),len(X.P[j] - X.P[i + 1])));
}
return res;
}
(2) 给定平面上
和求凸包直径差不多,但是在一条直线的基础上要维护三个点:一个在所枚举的直线对面的点、两个在不同侧面的点。
Polygon Max_Cover(Polygon Y) // 最小矩形覆盖
{
Polygon res;
double ans = 1e18;
int n = Y.n;
for(int i = 1,j = 2,x = 2,y = 2;i <= n;i++)
{
while(Triangle_Area(Y.P[i],Y.P[i + 1],Y.P[j]) < Triangle_Area(Y.P[i],Y.P[i + 1],Y.P[j % n + 1])) j = j % n + 1;
y = max(y,j);
while(x != j && dcmp((Y.P[i + 1] - Y.P[i]) ^ (Y.P[x + 1] - Y.P[x])) >= 0) x = x % n + 1;
while(y != i && dcmp((Y.P[i + 1] - Y.P[i]) ^ (Y.P[y + 1] - Y.P[y])) <= 0) y = y % n + 1;
Line l1 = {Y.P[i],Y.P[i + 1]};
Node p2 = Y.P[x],p3 = Y.P[j],p4 = Y.P[y],Ans[5];
Vector v = Vector(l1.B.x - l1.A.x,l1.B.y - l1.A.y),u = Normal(v);
Line l2 = {p2,p2 + u},l3 = {p3,p3 + v},l4 = {p4,p4 + u};
Ans[1] = Cross(l1,l2),Ans[2] = Cross(l2,l3),Ans[3] = Cross(l3,l4),Ans[4] = Cross(l4,l1);
double tmp = dabs(Ans[1] * Ans[2] + Ans[2] * Ans[3] + Ans[3] * Ans[4] + Ans[4] * Ans[1]) / 2;
if(ans > tmp)
{
ans = tmp;
for(int i = 1;i <= 4;i++) res.P[i] = Ans[i];
}
}
return res;
}
一条直线会把平面分成两部分,就是两个半平面。对于半平面,我们可以用直线方程式如:
如果多边形中存在一个区域使得在区域中可以看到多边形中任意位置(反之亦然),则这个区域就是多边形的核。多边形的核可以用半平面交来求解。
将所有直线极角排序,角度相同的保留下需要的一个;
用一个双端队列存储当前半平面交,每次通过判断队首与队尾第一个交点是否满足当前直线来更新;
先用队尾判定队首交点是否合法,再用队首判断队尾交点是否合法;
最后求出来的半平面交是一个凸多边形。
求出凸多边形后,就可以直接三角剖分求面积或者做别的操作了。
偶尔会出现半平面交不存在或面积为
Polygon HalfCut(Line *L,int n)
{
Line Q[N];Polygon S;
sort(L + 1,L + n + 1);
int m = n;n = 0;
for(int i = 1;i <= m;i++) if(i == 1 || dcmp(slope(L[i]) - slope(L[i - 1]))) L[++n] = L[i];
int h = 1,t = 0;
for(int i = 1;i <= n;i++)
{
while(h < t && check(L[i],Cross(Q[t],Q[t - 1]))) --t; // 当队尾两个直线交点不是在直线L[i]上或者左边时就出队
while(h < t && check(L[i],Cross(Q[h],Q[h + 1]))) ++h; // 当队头两个直线交点不是在直线L[i]上或者左边时就出队
Q[++t] = L[i];
}
while(h < t && check(Q[h],Cross(Q[t],Q[t - 1]))) --t;
while(h < t && check(Q[t],Cross(Q[h],Q[h + 1]))) ++h;
S.n = 0;for(int i = h;i <= t;i++) S.P[++S.n] = Cross(Q[i],Q[i < t ? i + 1 : h]);
return S;
}
两个多边形
闵可夫斯基和的边是由两凸包构成的,也就是说把两凸包的边极角排序后直接顺次连起来就是闵可夫斯基和。
由于凸包的优美性质,直接归并排序就好了。
Polygon operator + (Polygon X,Polygon Y) // 闵可夫斯基和
{
static Vector V1[N],V2[N];
Polygon Ans;
int n = X.n,m = Y.n;
for(int i = 1;i <= n;i++) V1[i] = X.P[i % n + 1] - X.P[i];
for(int i = 1;i <= m;i++) V2[i] = Y.P[i % m + 1] - Y.P[i];
int t = 0,i = 1,j = 1;
Ans.P[++t] = X.P[1] + Y.P[1];
while(i <= n && j <= m) ++t,Ans.P[t] = Ans.P[t - 1] + (dcmp(V1[i] * V2[j]) > 0 ? V1[i++] : V2[j++]);
while(i <= n) ++t,Ans.P[t] = Ans.P[t - 1] + V1[i++];
while(j <= m) ++t,Ans.P[t] = Ans.P[t - 1] + V2[j++];
Ans.n = t;return Ans;
}
Polygon operator + (Polygon X){return X;}
Polygon operator - (Polygon X){for(int i = 1;i <= X.n;i++) X.P[i].x = -X.P[i].x,X.P[i].y = -X.P[i].y;}
Polygon operator - (Polygon X,Polygon Y){return X + (-Y);}
void operator += (Polygon &X,Polygon Y){X = X + Y;}
void operator -= (Polygon &X,Polygon Y){X = X - Y;}
例题:P4557 [JSOI2018] 战争
简要题意:给出两个点集
若点集
那么
下面是本题的代码和模板:
#include<bits/stdc++.h>
#define Vector Node
using namespace std;
long long read()
{
long long s = 0,w = 1;
char ch = getchar();
while(ch < '0' || ch > '9')
{
if(ch == '-') w = -1;
ch = getchar();
}
while(ch >= '0' && ch <= '9')
{
s =(s << 1) +(s << 3) +(ch ^ 48);
ch = getchar();
}
return s * w;
}
void write(long long a)
{
if(a > 9) write(a / 10);
putchar(a % 10 + '0');
}
namespace Computational_geometry
{
const int N = 1e5 + 10;
const double eps = 1e-8,pi = 3.14159265358979;
int dcmp(double x){return x > eps ? 1 : x < -eps ? -1 : 0;} // 正负号
double dabs(double x){return x * dcmp(x);} // 绝对值
double S(double x){return x * x;} // 平方
struct Node // 点
{
double x,y;
Node(double X = 0,double Y = 0){x = X,y = Y;}
void read(){cin >> x >> y;} // 读入
void write(){cout << fixed << setprecision(2) << x << " " << y << endl;} // 输出
};
struct Line{Node A,B;};// 直线
struct Circle
{
Node O;double r;
Circle(Node P,double R = 0){O = P,r = R;}
};
struct Polygon
{
Node P[N];int n;
void read(){cin >> n;for(int i = 1;i <= n;i++) P[i].read();}
void write(){for(int i = 1;i <= n;i++) P[i].write();}
};// 多边形
Node operator + (Node a,Node b){return Node(a.x + b.x,a.y + b.y);} // 加
Node operator - (Node a,Node b){return Node(a.x - b.x,a.y - b.y);} // 减
Node operator * (Node a,double b){return Node(a.x * b,a.y * b);} // 数乘
Node operator / (Node a,double b){return Node(a.x / b,a.y / b);} // 除法
Node operator - (Node a){return Node(-a.x,-a.y);}
bool operator == (Node a,Node b){return !dcmp(a.x - b.x) && !dcmp(a.y - b.y);} // 点相等
double operator ^ (Vector a,Vector b){return a.x * b.x + a.y * b.y;} // 点乘
double operator * (Vector a,Vector b){return a.x * b.y - a.y * b.x;} // 叉乘
double len(Node a){return sqrt(a ^ a);} // 到原点的距离/模长
double sqr_len(Node a){return a ^ a;} // 到原点的距离平方/模长平方
Node Normal(Node a){return Node(-a.y,a.x);} // 法向量
double slope(Line l){return atan2(l.B.y - l.A.y,l.B.x - l.A.x);}
bool operator == (Line l,Line r){return (l.A == r.A && l.B == r.B) || (l.B == r.A && l.A == r.B);} // 直线相等
bool operator < (Line l,Line r){return dcmp(slope(l) - slope(r)) ? dcmp(slope(l) - slope(r)) < 0 : dcmp((r.B - r.A) * (l.A - r.A)) > 0;}
bool Node_on_Line(Node p,Line l){return !dcmp((p - l.A) * (l.B - l.A));} // 点在直线上
bool Node_on_Seg(Node p,Line l){return !dcmp((p - l.A) * (l.B - l.A)) && dcmp((p - l.A) ^ (p - l.B)) <= 0;} // 点在线段上
double Dis_Node_Node(Node a,Node b){return sqrt(S(a.x - b.x) + S(a.y - b.y));} // 点到点距离
double Dis_Node_Line(Node p,Line l) // 点到直线距离
{
Node a = l.A,b = l.B;
if(a == b) return len(p - a);
Vector x = Vector(p.x - a.x,p.y - a.y),y = Vector(p.x - b.x,p.y - b.y),z = Vector(b.x - a.x,b.y - a.y);
if(dcmp(x ^ z) < 0) return len(x);
if(dcmp(y ^ z) > 0) return len(y);
return dabs(x * z / len(z));// 面积除以底边长
}
double Seg_Len(Line l){return Dis_Node_Node(l.A,l.B);} // 线段长度
Node FootPoint(Node p,Line l) // 点到直线垂足
{
Node a = l.A,b = l.B;
Vector x = Vector(p.x - a.x,p.y - a.y),y = Vector(p.x - b.x,p.y - b.y),z = Vector(b.x - a.x,b.y - a.y);
double len1 = x ^ z / len(z),len2 = -1.0 * (y ^ z) / len(z);
return a + z * (len1 / (len1 + len2));
}
Node Symmetry_Node_Line(Node p,Line l){return p + (FootPoint(p,l) - p) * 2;} // 作一个点关于直线的对称点
Node Cross(Line l,Line r) // 两条线段交点
{
Node a = l.A,b = l.B,c = r.A,d = r.B;
Vector x = Vector(b.x - a.x,b.y - a.y),y = Vector(d.x - c.x,d.y - c.y),z = Vector(a.x - c.x,a.y - c.y);
return a + x * ((y * z) / (x * y));
}
bool Line_Cross_Seg(Line l,Line r){return Node_on_Seg(Cross(l,r),r);}
bool Seg_Cross_Seg(Line l,Line r){return Node_on_Seg(Cross(l,r),l) && Node_on_Seg(Cross(l,r),r);}
double Triangle_Area(Node a,Node b,Node c) // 三角形面积
{
Vector p = Vector(b.x - a.x,b.y - a.y),q = Vector(c.x - a.x,c.y - a.y);
return p * q;
}
double Polygon_Area(Polygon X) // 多边形面积
{
double res = 0;int n = X.n;
for(int i = 1;i <= n;i++) res += X.P[i] * X.P[i % n + 1];
return res / 2;
}
bool Node_in_Circle(Circle C,Node a){return dcmp(len(a - C.O) - C.r) <= 0;} // 点在圆内
int Node_in_Polygon(Polygon X,Node a) // 点在任意多边形以内
{
int cnt = 0,n = X.n;
double tmp;
for(int i = 1;i <= n;i++)
{
int j = i % n + 1;
if(Node_on_Seg(a,(Line){X.P[i],X.P[j]})) return 2;// 点在多边形上
if(a.y >= min(X.P[i].y,X.P[j].y) && a.y < max(X.P[i].y,X.P[j].y)) tmp = X.P[i].x + (a.y - X.P[i].y) / (X.P[j].y - X.P[i].y) * (X.P[j].x - X.P[i].x),cnt += dcmp(tmp - a.x) > 0;
}
return cnt & 1;
}
bool cmp_node(const Node A,const Node B){return dcmp(A * B) > 0 || (dcmp(A * B) == 0 && len(A) < len(B));}
int Node_in_ConvexPolygon(Polygon &X,Node a) // 点在凸多边形内
{
int n = X.n;
if(a * X.P[2] > 0 || X.P[n] * a > 0 || (a * X.P[2] == 0 && len(a) > len(X.P[2])) || (a * X.P[n] == 0 && len(a) > len(X.P[n]))) return 0;
int tot = lower_bound(X.P + 2,X.P + n + 1,a,cmp_node) - X.P - 1;
return dcmp((a - X.P[tot]) * (X.P[tot % n + 1] - X.P[tot])) <= 0;
}
int Line_in_Polygon(Polygon X,Line l) // 线段在任意多边形以内
{
if(Node_in_Polygon(X,l.A) == 0 || Node_in_Polygon(X,l.B) == 0) return 0;
int n = X.n;
for(int i = 1;i <= n;i++)
{
int j = i % n + 1;
if(Seg_Cross_Seg(l,(Line){X.P[i],X.P[j]})) return 0;
if(!dcmp(Dis_Node_Line(l.A,(Line){X.P[i],X.P[j]})) && !dcmp(Dis_Node_Line(l.B,(Line){X.P[i],X.P[j]}))) return 2;// 线段在多边形上
}
return 1;
}
int Line_in_ConvexPolygon(Polygon X,Line l){return Node_in_ConvexPolygon(X,l.A) && Node_in_ConvexPolygon(X,l.B);} // 线段在凸多边形以内
bool check_polygon(Polygon X,Polygon Y) // 两个多边形是否相离
{
int n = X.n,m = Y.n;
for(int i = 1;i <= n;i++)
{
for(int j = 1;j <= m;j++)
{
if(Seg_Cross_Seg((Line){X.P[i],X.P[i % n + 1]},(Line){Y.P[j],Y.P[j % m + 1]})) return 0;
if(Node_in_Polygon(Y,X.P[i]) || Node_in_Polygon(X,Y.P[j])) return 0;
}
}
return 1;
}
bool cmp_Node(Node a,Node b){return a.x == b.x ? a.y < b.y : a.x < b.x;}
Polygon ConvexHull(Polygon X) // 求凸包
{
int n = X.n,t = 0;Polygon Ans;
sort(X.P + 1,X.P + n + 1,cmp_Node);
for(int i = 1;i <= n;i++) // 下凸包
{
while(t > 1 && dcmp((Ans.P[t] - Ans.P[t - 1]) * (X.P[i] - Ans.P[t - 1])) <= 0) --t;
Ans.P[++t] = X.P[i];
}
int St = t;
for(int i = n - 1;i >= 1;i--) // 上凸包
{
while(t > St && dcmp((Ans.P[t] - Ans.P[t - 1]) * (X.P[i] - Ans.P[t - 1])) <= 0) --t;
Ans.P[++t] = X.P[i];
}
Ans.P[t] = (Node){0,0},Ans.n = t - 1; // 减一因为最后的 P1 被统计了两遍
return Ans;
}
double Convex_Diameter(Polygon X) // 凸包直径
{
int n = X.n;
double res = len(X.P[2] - X.P[1]);
if(n <= 2){return res;}
if(n == 3){return max(len(X.P[2] - X.P[1]),max(len(X.P[3] - X.P[2]),len(X.P[3] - X.P[1])));}
for(int i = 1,j = 3;i <= n;i++)
{
while(dcmp((X.P[i + 1] - X.P[i]) * (X.P[j] - X.P[i]) - (X.P[i + 1] - X.P[i]) * (X.P[j + 1] - X.P[i])) < 0) j = j % n + 1;
res = max(res,max(len(X.P[j] - X.P[i]),len(X.P[j] - X.P[i + 1])));
}
return res;
}
Circle getCircle(Node A,Node B,Node C) // 三点确定一圆
{
double x1 = A.x,y1 = A.y,x2 = B.x,y2 = B.y,x3 = C.x,y3 = C.y;
double D = ((S(x2) + S(y2) - S(x3) - S(y3)) * (y1 - y2) - (S(x1) + S(y1) - S(x2) - S(y2)) * (y2 - y3)) / ((x1 - x2) * (y2 - y3) - (x2 - x3) * (y1 - y2));
double E = (S(x1) + S(y1) - S(x2) - S(y2) + D * (x1 - x2)) / (y2 - y1);
double F = -(S(x1) + S(y1) + D * x1 + E * y1);
return Circle(Node(-D / 2.0,-E / 2.0),sqrt((S(D) + S(E) - 4.0 * F) / 4.0));
}
Circle getcircle(Node A,Node B,Node C) // 三点确定一圆
{
Node P1 = (A + B) / 2,P2 = (A + C) / 2;
Node O = Cross((Line){P1,P1 + Normal(B - A)},(Line){P2,P2 + Normal(C - A)});
return Circle(O,len(A - O));
}
void Random(Polygon X){for(int i = 1;i <= X.n;i++) swap(X.P[i],X.P[rand() % X.n + 1]);}
Circle Min_Circle_Cover(Polygon X) // 最小圆覆盖
{
int n = X.n;Random(X);
Circle C = Circle(X.P[1],0);
for(int i = 2;i <= n;i++)
{
if(Node_in_Circle(C,X.P[i]) == 0)
{
C = Circle(X.P[i],0);
for(int j = 1;j < i;j++)
{
if(Node_in_Circle(C,X.P[j]) == 0)
{
C.O = (X.P[i] + X.P[j]) / 2,C.r = len(X.P[j] - C.O);
for(int k = 1;k < j;k++) if(Node_in_Circle(C,X.P[k]) == 0) C = getcircle(X.P[i],X.P[j],X.P[k]);
}
}
}
}
return C;
}
Polygon Max_Cover(Polygon Y) // 最小矩形覆盖
{
Polygon res;
double ans = 1e18;
int n = Y.n;
for(int i = 1,j = 2,x = 2,y = 2;i <= n;i++)
{
while(Triangle_Area(Y.P[i],Y.P[i + 1],Y.P[j]) < Triangle_Area(Y.P[i],Y.P[i + 1],Y.P[j % n + 1])) j = j % n + 1;
y = max(y,j);
while(x != j && dcmp((Y.P[i + 1] - Y.P[i]) ^ (Y.P[x + 1] - Y.P[x])) >= 0) x = x % n + 1;
while(y != i && dcmp((Y.P[i + 1] - Y.P[i]) ^ (Y.P[y + 1] - Y.P[y])) <= 0) y = y % n + 1;
Line l1 = {Y.P[i],Y.P[i + 1]};
Node p2 = Y.P[x],p3 = Y.P[j],p4 = Y.P[y],Ans[5];
Vector v = Vector(l1.B.x - l1.A.x,l1.B.y - l1.A.y),u = Normal(v);
Line l2 = {p2,p2 + u},l3 = {p3,p3 + v},l4 = {p4,p4 + u};
Ans[1] = Cross(l1,l2),Ans[2] = Cross(l2,l3),Ans[3] = Cross(l3,l4),Ans[4] = Cross(l4,l1);
double tmp = dabs(Ans[1] * Ans[2] + Ans[2] * Ans[3] + Ans[3] * Ans[4] + Ans[4] * Ans[1]) / 2;
if(ans > tmp)
{
ans = tmp;
for(int i = 1;i <= 4;i++) res.P[i] = Ans[i];
}
}
return res;
}
int check(Line l,Node A) {return dcmp((A - l.A) * (l.B - l.A)) > 0;} // 判断点 A 是否在直线 l 的右边
Polygon HalfCut(Line *L,int n) // 半平面交
{
Line Q[N];Polygon S;
sort(L + 1,L + n + 1);
int m = n;n = 0;
for(int i = 1;i <= m;i++) if(i == 1 || dcmp(slope(L[i]) - slope(L[i - 1]))) L[++n] = L[i];
int h = 1,t = 0;
for(int i = 1;i <= n;i++)
{
while(h < t && check(L[i],Cross(Q[t],Q[t - 1]))) --t; // 当队尾两个直线交点不是在直线L[i]上或者左边时就出队
while(h < t && check(L[i],Cross(Q[h],Q[h + 1]))) ++h; // 当队头两个直线交点不是在直线L[i]上或者左边时就出队
Q[++t] = L[i];
}
while(h < t && check(Q[h],Cross(Q[t],Q[t - 1]))) --t;
while(h < t && check(Q[t],Cross(Q[h],Q[h + 1]))) ++h;
S.n = 0;for(int i = h;i <= t;i++) S.P[++S.n] = Cross(Q[i],Q[i < t ? i + 1 : h]);
return S;
}
Polygon operator + (Polygon X,Polygon Y) // 闵可夫斯基和
{
static Vector V1[N],V2[N];
Polygon Ans;
int n = X.n,m = Y.n;
for(int i = 1;i <= n;i++) V1[i] = X.P[i % n + 1] - X.P[i];
for(int i = 1;i <= m;i++) V2[i] = Y.P[i % m + 1] - Y.P[i];
int t = 0,i = 1,j = 1;
Ans.P[++t] = X.P[1] + Y.P[1];
while(i <= n && j <= m) ++t,Ans.P[t] = Ans.P[t - 1] + (dcmp(V1[i] * V2[j]) >= 0 ? V1[i++] : V2[j++]);
while(i <= n) ++t,Ans.P[t] = Ans.P[t - 1] + V1[i++];
while(j <= m) ++t,Ans.P[t] = Ans.P[t - 1] + V2[j++];
Ans.n = t;return Ans;
}
Polygon operator + (Polygon X){return X;}
Polygon operator - (Polygon X){Polygon Y;Y.n = X.n;for(int i = 1;i <= X.n;i++) Y.P[i] = -X.P[i];return Y;}
Polygon operator - (Polygon X,Polygon Y){return X + (-Y);}
void operator += (Polygon &X,Polygon Y){X = X + Y;}
void operator -= (Polygon &X,Polygon Y){X = X - Y;}
}using namespace Computational_geometry;
Polygon X,Y,Z,CX,CY,CZ;
Node a;
long long q;
int main()
{
// freopen("input.in","r",stdin);
X.n = read(),Y.n = read(),q = read();
for(int i = 1;i <= X.n;i++) X.P[i].read();
for(int i = 1;i <= Y.n;i++) Y.P[i].read();
CX = ConvexHull(X),CY = ConvexHull(-Y),Z = CX + CY,CZ = ConvexHull(Z);
Node st = CZ.P[1];for(int i = 1;i <= CZ.n;i++) CZ.P[i] = CZ.P[i] - st;
while(q--) a.read(),cout << Node_in_ConvexPolygon(CZ,a - st) << endl;
return 0;
}