В этом году проходил BSUIR Open XIV, мне было очень приятно съездить в Минск и на само соревнование. Но еще больше мне понравилась задачу, которую я туда предложил. В целом я не видел эту идею раньше, но она кажется достаточно простой, чтобы появиться раньше на каких-нибудь соревнованиях. Так что если вы знаете, где раньше встречалась это идея, то поделитесь, пожалуйста.
Задача. (*BSUIR OPEN XIV*) Дано клеточное поле $$$100 \times 100$$$, в котором между некоторыми клетками проведены стены, также стены проведены по границе поля. Луч запускается из центра клетки $$$(r,c)$$$ в направлении $$$(x,y)$$$. Требуется определить, в какой клетке он окажется, пройдя расстояние $$$\sqrt{x^2+y^2}$$$, если луч отражается от стен.
Решение задачки
Взглянем на вектор $$$(x,y)$$$ и посмотрим, в каком порядке он пересекает вертикальные и горизонтальные линии сетки. В точке $$$(0,0)$$$ пересечение с линиями сетки учитывать не будем, но будем учитывать пересечения в точке $$$(x,y)$$$, причем по формальным причинам мы будем считать, что сначала происходит пересечение с вертикальной прямой.

Последовательность пересечений можно записать в виде строки:
- $$$R$$$ обозначает пересечение вертикальной линии сетки (перемещение на клетку вправо),
- $$$U$$$ обозначает пересечение горизонтальной линии сетки.
Тогда каждому вектору $$$(x,y)$$$ можно сопоставить строку $$$s(x, y)$$$, содержащую последовательность этих пересечений. Например,
Предположим, что $$$x \ge y$$$. Это означает, что вектор «пологий», и перед каждым пересечением горизонтальной линии происходит пересечение вертикальной. А именно, перед $$$k$$$-м символом U стоит
подряд идущих символов R.
Таким образом, перед каждым символом U стоит блок, состоящий как минимум из $$$\left\lfloor x/y \right\rfloor$$$ символов R; эти блоки вместе с символами U можно сжать в один символ. Например,
Таким образом, можно предположить, что если $$$s(x, y, R, U)$$$ — это строка $$$s(x,y)$$$, где на месте символов R и U стоят строки $$$R$$$ и $$$U$$$, то верно равенство:
Ясно, что это правда. Например, в случае $$$x \ge y$$$ достаточно убедиться в том, что перед $$$k$$$-м символом $$$U$$$ после подстановки сохраняется количество подряд идущих букв R:
Таким образом, строку $$$s(x,y)$$$ можно вычислять с помощью алгоритма Евклида. Разумеется, в общем случае нам нужна не строка, а какая-то более полезная информация. Символы R и U могут принадлежать произвольному моноиду, для которого можно эффективно вычислять следующие операции:
- произведение элементов;
- возведение элемента в степень.
Например, в качестве моноида могут выступать перестановки, матрицы или любые другие объекты, которые можно поддерживать в дереве отрезков. В частности, в подстроке $$$s(x,y)$$$ можно поддерживать количество подпоследовательностей UR, что соответствует вычислению значения floor_sum (только в таком случае надо аккуратно разобраться с приоритетами символов R и U).
Обобщённо алгоритм трекинга вектора может быть реализован следующим образом:
Другие обобщения
В ходе алгоритма можно строить не композицию моноидов $$$R$$$ и $$$U$$$, а последовательность неполных частных вместе со всеми промежуточными вычислениями. Например, при входных данных $$$(x,y)$$$ таблица вычислений будет выглядеть следующим образом. Напомним, что
| Шаг | Вычисленное значение | $$$q$$$ | $$$x$$$ | $$$y$$$ |
|---|---|---|---|---|
| 1 | RU | 1 | 4 | 3 |
| 2 | RURUR | 2 | 1 | 3 |
| 3 | RURURRU | 1 | 1 | 1 |
Работая со всеми промежуточными вычислениями, можно дополнительно обрабатывать следующие сценарии:
- композицию моноидов $$$R$$$ и $$$U$$$ на любом подотрезке $$$(l,r)$$$ строки $$$s(x,y)$$$.
Так, если запускать вектор не из точки $$$(0,0)$$$, а из произвольной точки $$$(\alpha, \beta)$$$, то может получиться строка $$$s(x,y,\alpha,\beta)$$$, отличная от $$$s(x,y)$$$. Но также ясно, что строка $$$s(x,y,\alpha,\beta)$$$ получается циклическим сдвигом строки $$$s(x,y)$$$.
Чтобы понять, что именно это будет за циклический сдвиг, вспомним, что строку $$$s$$$ можно представить в виде объединения блоков вида R...RU, где количество букв R перед $$$k$$$-й буквой U вычисляется по формуле:
Таким образом, если вектор запускается из произвольной точки $$$(\alpha/y, 0)$$$, то длина $$$k$$$-го блока будет вычисляться по похожей формуле:
В случае взаимной простоты $$$x$$$ и $$$y$$$ можно сделать замену
И тогда число $$$k_0$$$ и будет искомым циклическим сдвигом.
Таким образом, предлагаемый алгоритм является универсальным (и, вероятно, не лучшим по константе времени исполнения) способом для решения многих задач-обобщений floor_sum. В частности,
можно вычислить за время
в общем случае, что достаточно приятно.









Автокомментарий: текст был обновлен пользователем teraqqq (предыдущая версия, новая версия, сравнить).
Auto comment: topic has been updated by teraqqq (previous revision, new revision, compare).
Great problem!
I want to describe another look at this problem.
Consider the ray $$$(0, 0) \to (x, y)$$$. It would be nice to perturb it a bit, so that the coordinates of the ray would simplify. Consider the expansion of $$$\frac{x}{y}$$$ into a continued fraction, and let $$$\frac{p_k}{q_k}$$$ be the convergents with $$$(p_n, q_n) = (x, y)$$$. Then the vectors $$$\overrightarrow{r_k} = (p_k, q_k)$$$ are best approximations for the vector $$$(x, y)$$$ (so called "nose stretching algorithm"). More formally, $$$(x, y) = (p_{n-2}, q_{n-2}) + a_{n-1}(p_{n-1}, q_{n-1})$$$, and there are no integer points inside the triangle.
For example, consider the picture (taken from this great article at cp-algorithms about continued fractions):
Here we have $$$(9, 7) = (1, 1) + 2\cdot(4, 3)$$$, and there are no integer points inside the upper triangle.
Therefore, to trace the ray $$$(x, y)$$$ you only need to be able to trace the rays $$$(p_{n-2}, q_{n-2})$$$ and $$$(p_{n-1}, q_{n-1})$$$, which can be done recursively. Some care is required to resolve ties when the ray passes through the corners, though. Of course, the convergents for convergents are the convergents for the initial fraction, so the recursive function will try to trace only $$$\log C$$$ different rays.
The complexity is $$$O(nm\log C)$$$, since we can apply memoization. It is the same as of yours solution, and the idea is, surely, the same too, since the continues fraction expansion is essentially the Euclidian algorithm. However I thought that this visualization (and link to the nose stretching algorithm) would be nice.