2299. Теодор Рузвельт

 

"Теодор Рузвельт" – флагман военно-морского флота Кукуляндии. Заклятые враги кукуляндцев, флатландцы, решили уничтожить его. Они узнали, что "Теодор Рузвельт" представляет собой выпуклый многоугольник из n вершин и узнали его координаты. Затем они выпустили m баллистических ракет и определили координаты точек, где эти ракеты взорвались. По расчетам штаба флатландцев, "Теодор Рузвельт" будет уничтожен, если в него попадёт хотя бы k ракет. Вычислите, удалось ли флатландцам уничтожить корабль.

 

Вход. В первой строке записаны целые числа n, m, k (3 ≤ n ≤ 105, 0 ≤ km ≤ 105). В следующих n строках записаны координаты вершин многоугольника в порядке обхода против часовой стрелки. В следующих m строках записаны координаты точек. Гарантируется, что все координаты – целые числа, не превосходящие по модулю 109.

 

Выход. Выведите YES, если в многоугольнике лежит по крайней мере k точек, и NO в противном случае.

 

Пример входа

Пример выхода

5 4 2

1 -1

1 2

0 4

-1 2

-1 -1

-2 -1

1 -1

0 1

2 3

YES

 

 

РЕШЕНИЕ

геометрия + бинарный поиск

 

Анализ алгоритма

Рассмотрим, как решить задачу принадлежности точки выпуклому многоугольнику.

Координаты вершин многоугольника заданы в порядке обхода против часовой стрелки: p1, p2, …, pn. Рассмотрим первую точку многоугольника p1. Относительно нее все остальные точки будут отсортированы по углу. Проведём от точки p1 все лучи, содержащие диагонали. Бинарным поиском ищем такое ребро pipi+1, что повороты p1piq и p1pi+1q различны. Таким образом мы найдем область, в которой лежит точка q.

Когда найден угол pip1pi+1, в котором находится точка q, за константное время можно проверить, с одной ли стороны от прямой pipi+1 лежат точки p1 и q. Для этого поворот pipi+1q должен быть левым (поворот pipi+1p1 является левым, так как исходные точки многоугольника заданы в порядке обхода против часовой стрелки).

В начале алгоритма следует проверить, не является ли поворот p1pnq левым, или поворот p1p2q правым. Если выполняется хотя бы одно из этих условий, то q лежит вне многоугольника.

 

Направление поворота. Пусть совершается движение от точки А до В, затем от В до С. При движении имеет место левый поворот (движение происходит против часовой стрелки), если AB ´ BC > 0 (псевдоскалярное произведение векторов положительно) и правый, если AB ´ BC < 0.

 

Реализация алгоритма

Объявим структуру точка и массив точек.

 

#define MAX 100001

struct Point

{

  long long x, y;

  Point(long long x = 0, long long y = 0) : x(x), y(y) {}

};

Point p[MAX];

 

 

Функция angle определяет левый или правый поворот происходит при движении по точкам А , B, С. Вычислим вектора:

AB = (b.x – a.x, b.y – a.y)

BC = (c.x – b.x, c.y – b.y)

Псевдоскалярное произведение AB x BC равно

 = (b.x – a.x) * (c.y – b.y) – (b.y – a.y) * (c.x – b.x)

и его знак определяет направление поворота:

·        положительный – левый поворот;

·        отрицательный – правый поворот;

 

int angle(Point a, Point b, Point c)

{

  long long q = (b.x - a.x) * (c.y - b.y) - (b.y - a.y) * (c.x - b.x);

  return (q > 0) - (q < 0); // sgn(q)

}

 

Функция inside возвращает true, если точка q лежит внутри многоугольника.

 

bool inside(Point q)

{

 

Первую точку выберем базовой.

 

  Point a = p[1];

 

Если поворот p1pnq левый, или поворот p1p2q правый, то точка q лежит вне угла pnp1p2 и соответственно вне многоугольника.

 

  if (angle(a, p[2], q) < 0 || angle(a, p[n], q) > 0) return false;

 

Стартуем бинарный поиск на отрезке [l; r] = [2; n]. Ищем такие точки pl и pr (r = l + 1) что точка q лежит внутри угла plp1pr.

 

  int l = 2, r = n, m;

  while (l + 1 < r)

  {

    m = (l + r) / 2;

    if (angle(a, p[m], q) < 0)

      r = m;

    else

      l = m;

  }

  return angle(p[l], p[r], q) >= 0;

}

 

Основная часть программы. Читаем входные данные.

 

scanf("%d %d %d", &n, &m, &k);

for (int i = 1; i <= n; i++)

{

  scanf("%lld %lld", &x, &y);

  p[i] = Point(x, y);

}

 

Вычисляем количество точек, лежащих внутри многоугольника.

 

res = 0;

for (int i = 1; i <= m; ++i)

{

  scanf("%lld %lld", &x, &y);

  if (inside(Point(x, y))) res++;

}

 

Выводим ответ.

 

if (res < k) puts("NO"); else puts("YES");