Description
方师傅来到了一个二维平面。他站在原点上,觉得这里风景不错,就建了一个房子。这个房子是n个点的凸多边形,原点一定严格在凸多边形内部。有m个人也到了这个二维平面。现在你得到了m个人的坐标,你要判断这m个人中有多少人在房子内部。点在凸多边形边上或者内部都认为在房子里面。
第一行一个数n,接下来n行,每行两个整数x,y。输入按照逆时针顺序输入一个凸包。
接下来一个数m,最后有m行,第一行两个整数 x,y,表示第一个人的坐标。
对于第i个询问(i>=2) ,输入两个数dx,dy。
如果上一个人在房子内部,x[i]=x[i-1]+dx,y[i]=y[i-1]+dy。否则x[i]=x[i-1]-dx,y[i]=y[i-1]-dy。
n <= 100000, m <= 200000,输入保证所有人的坐标,房屋的坐标都在[-1e9,1e9]之内。
Solution
计算几何只会凸包系列
先回顾一下什么是叉积。
如上图,我们有两个红色向量共起点。它们的叉积为 ∣ a ⃗ ∣ ∣ b ⃗ ∣ ⋅ sin θ |{\vec{a}}| |{\vec{b}}|\cdot\sin\theta ∣a∣∣b∣⋅sinθ,其中夹角为图中绿色部分,可知叉积有正负。这里夹角的范围是 ( 0 , 2 π ) (0,2\pi) (0,2π)
以上图为栗。一个比较显然的做法就是我们选取两条邻边,待判断点在凸多边形内必有它在这两条邻边组成的两个半平面的交内(长&绕
于是我们得到一个O(n)的做法,只需要枚举每个顶点以及两邻边做这个即可
考虑优化这个东西。我们选取任意一个点(莉如图中红点),做三角剖分。待判断点在多边形内必有它在某一三角形内部。我们二分所在三角形的一条边,用上面的方法判断是否在三角形内就行了
Code
#include <stdio.h>
#include <string.h>
#define rep(i,st,ed) for (int i=st;i<=ed;++i)
const int N=200005;
struct pos {
double x,y;
pos operator -(pos b) const {return (pos) {x-b.x,y-b.y};}
pos operator +(pos b) const {return (pos) {x+b.x,y+b.y};}
double operator *(pos b) const {return x*b.y-y*b.x;}
} p[N],s,pre;
double cros(pos a,pos b,pos c) {
return (a-c)*(b-c);
}
int main(void) {
int n,ans=0; scanf("%d",&n);
rep(i,1,n) scanf("%lf%lf",&p[i].x,&p[i].y);
bool flag=false,first=false;
int m; for (scanf("%d",&m);m--;) {
scanf("%lf%lf",&s.x,&s.y);
if (first) {
if (flag) s=s+pre;
else s=pre-s;
} else first=true;
flag=false;
if (cros(p[2],s,p[1])>=0&&cros(p[n],s,p[1])<=0) {
int l=2,r=n;
for (;l<=r;) {
int mid=(l+r)>>1;
if (cros(p[mid],s,p[1])>0) {
l=mid+1;
} else r=mid-1;
}
if (cros(p[1],s,p[l])>=0&&cros(p[l-1],s,p[l])<=0) {
flag=true;
}
}
ans+=flag; pre=s;
}
printf("%d\n", ans);
return 0;
}