Comprobar si un punto pertenece al polígono convexo en
Consideremos el siguiente problema: nos dan un polígono convexo con vértices enteros y muchas consultas. Cada consulta es un punto, para el cual debemos determinar si yace dentro o en el borde del polígono o no. Supongamos que el polígono está ordenado en sentido antihorario. Responderemos cada consulta en de forma online.
Algoritmo
Elijamos el punto con la menor coordenada x. Si hay varios, elegimos el de menor coordenada y. Denotémoslo como . Ahora todos los demás puntos del polígono están ordenados por su ángulo polar desde el punto elegido (porque el polígono está ordenado en sentido antihorario).
Si el punto pertenece al polígono, pertenece a algún triángulo (tal vez más de uno si yace en el borde de los triángulos). Consideremos el triángulo tal que pertenece a este triángulo e es máximo entre todos esos triángulos.
Hay un caso especial. yace en el segmento . Este caso lo comprobaremos por separado. En caso contrario todos los puntos con están en sentido antihorario desde respecto de , y todos los demás puntos no están en sentido antihorario desde . Esto significa que podemos aplicar búsqueda binaria para el punto , tal que no está en sentido antihorario desde respecto de , e es máximo entre todos esos puntos. Y después comprobamos si el punto está realmente en el triángulo determinado.
El signo de nos dirá si el punto está en sentido horario o antihorario desde el punto respecto del punto . Si , entonces el punto está a la derecha del vector que va de a , lo que significa en sentido horario desde respecto de . Y si , entonces el punto está a la izquierda, o en sentido antihorario. Y está exactamente en la recta entre los puntos y .
Volviendo al algoritmo: Consideremos un punto de consulta . Primero, debemos comprobar si el punto yace entre y . En caso contrario ya sabemos que no puede ser parte del polígono. Esto se puede hacer comprobando si el producto cruz es cero o tiene el mismo signo que , y es cero o tiene el mismo signo que . Luego manejamos el caso especial en el que es parte de la recta . Y luego podemos hacer búsqueda binaria del último punto de que no está en sentido antihorario desde respecto de . Para un solo punto esta condición se puede comprobar verificando que . Después de encontrar tal punto , debemos probar si yace dentro del triángulo . Para probar si pertenece al triángulo, podemos simplemente comprobar que . Esto comprueba si el área del triángulo tiene exactamente el mismo tamaño que la suma de los tamaños del triángulo , el triángulo y el triángulo . Si está fuera, entonces la suma de esos tres triángulos será mayor que el tamaño del triángulo. Si está dentro, entonces será igual.
Implementación
La función prepare se asegurará de que el punto lexicográficamente más pequeño (menor valor x, y en empates menor valor y) sea , y computa los vectores .
Después la función pointInConvexPolygon computa el resultado de una consulta.
Además recordamos el punto y trasladamos todos los puntos consultados con él para computar la distancia correcta, ya que los vectores no tienen un punto inicial.
Al trasladar los puntos de consulta podemos asumir que todos los vectores empiezan en el origen , y simplificar los cómputos de distancias y longitudes.
struct pt {
long long x, y;
pt() {}
pt(long long _x, long long _y) : 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); }
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); }
};
bool lexComp(const pt &l, const pt &r) {
return l.x < r.x || (l.x == r.x && l.y < r.y);
}
int sgn(long long val) { return val > 0 ? 1 : (val == 0 ? 0 : -1); }
vector<pt> seq;
pt translation;
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;
}
void prepare(vector<pt> &points) {
n = points.size();
int pos = 0;
for (int i = 1; i < n; i++) {
if (lexComp(points[i], points[pos]))
pos = i;
}
rotate(points.begin(), points.begin() + pos, points.end());
n--;
seq.resize(n);
for (int i = 0; i < n; i++)
seq[i] = points[i + 1] - points[0];
translation = points[0];
}
bool pointInConvexPolygon(pt point) {
point = point - translation;
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 - 1;
while (r - l > 1) {
int mid = (l + r) / 2;
int pos = mid;
if (seq[pos].cross(point) >= 0)
l = mid;
else
r = mid;
}
int pos = l;
return pointInTriangle(seq[pos], seq[pos + 1], pt(0, 0), point);
}