Задано
подвешенное дерево, содержащее n (1
≤ n ≤ 105)
вершин, пронумерованных от 0 до n –
1. Требуется ответить на m (1 ≤
m ≤ 107) запросов о
наименьшем общем предке для пары вершин.
Запросы
генерируются следующим образом. Заданы числа a1, a2
и числа x, y и z. Числа a3, ..., a2m
генерируются следующим образом:
ai = (x * ai-2
+ y * ai-1 + z)
mod n
Первый запрос
имеет вид (a1, a2). Если ответ на (i – 1)-ый запрос равен v, то i-ый запрос имеет вид ((a2i-1 + v) mod n, a2i).
Вход. Первая строка
содержит два числа n и m. Корень дерева имеет номер 0. Вторая
строка содержит n – 1 целых чисел, i-е из этих чисел равно номеру родителя
вершины i. Третья строка содержит два
целых числа в диапазоне от 0 до n –
1: a1 и a2. Четвертая строка содержит
три целых числа: x, y и z,
эти числа неотрицательны и не превосходят 109.
Выход. Выведите сумму
номеров вершин – ответов на все запросы.
Пример входа 1 |
Пример выхода 1 |
3 2 0 1 2 1 1 1 0 |
2 |
|
|
Пример входа 2 |
Пример выхода 2 |
1 2 0 0 1 1 1 |
0 |
РЕШЕНИЕ
LCA – двоичный подъем
В задаче следует
реализовать запросы о наименьшем общем предке для пар вершин. Дерево задано,
оно не меняется в процессе запросов. Воспользуемся алгоритмом двоичного
подъема.
Объявим рабочие массивы для алгоритма двоичного подъема.
#define MAX
100010
#define LOGMAX
18
vector<int> g[MAX];
int
d[MAX], f[MAX];
int
up[MAX][LOGMAX];
Поиск в глубину – построение
массива up.
void dfs (int v, int p = 0, int len = 0)
{
int i, to;
d[v] = timer++;
up[v][0] = p;
for(i = 1; i <= l; i++)
up[v][i] =
up[up[v][i-1]][i-1];
for(i = 0; i < g[v].size(); i++)
{
to = g[v][i];
if (to != p) dfs (to, v, len + 1);
}
f[v] = timer++;
}
Проверяем, является ли a отцом b.
int
Parent(int a, int
b)
{
return (d[a] <= d[b]) && (f[a] >=
f[b]);
}
Вычисление наименьшего
общего предка.
int LCA (int a, int b)
{
if (Parent(a, b)) return
a;
if (Parent(b, a)) return
b;
for (int i = l; i
>= 0; i--)
if (!Parent(up[a][i], b)) a = up[a][i];
return up[a][0];
}
Основная часть программы.
Читаем входные данные.
scanf("%d %d",&n,&m);
Найдем такое наименьшее l, для которого 2l > n.
l = 1;
while ((1
<< l) <= n) l++;
Читаем и строим дерево.
for(i = 1;
i <= n - 1; i++)
{
scanf("%d",&v);
g[v].push_back(i);
g[i].push_back(v);
}
Запускаем поиск в глубину –
алгоритм двоичного подъема.
timer = 0;
dfs(0);
Читаем данные для генерации
запросов.
scanf("%d %d",&a1,&a2);
scanf("%lld %lld %lld",&x,&y,&z);
Генерируем запросы, выполняем
их и вычисляем необходимую сумму.
v = sum = 0;
for(i = 0;
i < m; i++)
{
v = LCA((a1 + v) %
n, a2);
sum += v;
a1 = (x * a1 + y *
a2 + z) % n;
a2 = (x * a2 + y *
a1 + z) % n;
}
Выводим ответ.
printf("%lld\n",sum);