Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /*
- Задано дерево с корнем, содержащее 𝑛 (1 ≤ 𝑛 ≤ 100 000) вершин, пронумерованных от 0 до 𝑛−1.
- Требуется ответить на 𝑚 (1 ≤ 𝑚 ≤ 10 000 000) запросов о наименьшем общем предке для пары вершин.
- Запросы генерируются следующим образом. Заданы числа 𝑎1, 𝑎2 и числа 𝑥, 𝑦 и 𝑧.
- Числа 𝑎3, . . . , 𝑎2𝑚 генерируются следующим образом: 𝑎𝑖 = (𝑥·𝑎𝑖−2+𝑦·𝑎𝑖−1+𝑧) mod 𝑛.
- Первый запрос имеет вид ⟨𝑎1, 𝑎2⟩.
- Если ответ на 𝑖−1-й запрос равен 𝑣, то 𝑖-й запрос имеет вид ⟨(𝑎2𝑖−1 + 𝑣) mod 𝑛, 𝑎2𝑖⟩.
- Для решения задачи можно использовать метод двоичного подъёма.
- Формат входных данных:
- ---Первая строка содержит два числа: 𝑛 и 𝑚. Корень дерева имеет номер 0.
- ---Вторая строка содержит 𝑛 − 1 целых чисел, 𝑖-е из этих чисел равно номеру родителя вершины 𝑖.
- ---Третья строка содержит два целых числа в диапазоне от 0 до 𝑛−1: 𝑎1 и 𝑎2.
- ---Четвертая строка содержит три целых числа: 𝑥, 𝑦 и 𝑧, эти числа неотрицательны и не превосходят 109.
- Формат выходных данных:
- ---Выведите в выходной файл сумму номеров вершин — ответов на все запросы.
- */
- /*
- Решение:
- -сохраняем дерево в список смежности
- -создаем пустой массив вершин (way) - 'хронологию' обхода
- -создаем массив индексов (pos) - pos[i] - первое вхождение вершины i в массив way
- -обходим дерево в глубину. Во время обхода, попав в очередную вершину(в том числе и корень):
- --если мы попали в вершину впервые:
- ---записываем ее в массив way (ее номер и ее глубину)
- ---записываем ее индекс в массиве way(хронологии) в массив pos(индексов)
- --иначе(попали в вершину повторно, вернувшись из рекурсии)
- ---записываем ее в массив way (кажый раз когда возвращаемся в нее)
- -после обхода хронология (массив way) содержит (степень вершины)
- --экземпляров каждой вершины в порядке обхода
- ----------
- -заметим интересную вещь. Рассмотрим участок хронологии от pos[a] до pos[b] включительно
- --в нем хранится хронология обхода от первого посещения a до первого посещения b
- --а значит в нем хранятся:
- ---все дети(и дети детей и т.д.) a и b
- ---все предки a и b вплоть до первого общего предка
- --то есть достаточно пройтись по участку от way[pos[a]] до way[pos[b]]
- ---и найти вершину с наимельшей глубиной. Это и будет LCA для a и b
- ----------
- -заносим way в sparce table
- -готовимся искать RMQ
- -далее на запрос a, b отвечаем RMQ(way[min(pos[a],pos[b])], way[max(pos[a],pos[b])])
- */
- #include <iostream>
- #include <vector>
- #include <algorithm>
- #include <utility>
- #include <cassert>
- using namespace std;
- //в sparse table будут храниться не только числа(минимумы), но и их индексы в массиве
- // это нужно для удобства поиска 2 минимума
- //element хранятся в sparse_table
- struct element {
- //номер вершины
- int num;
- //глубина вершины
- int depth;
- //конструктор от значения и индекса
- element(int _num = 0, int _depth = -1) : num(_num), depth(_depth) {}
- //конструктор от другого element
- element(const element& elem) : num(elem.num), depth(elem.depth) {}
- //операторы = и <
- element& operator=(const element& right) {
- num = right.num; depth = right.depth;
- return *this;
- }
- bool operator<(const element& right) const {
- return depth < right.depth;
- }
- };
- //Разреженная таблица
- class sparce_table {
- private:
- //длина отрезка
- int N;
- //логарифм от длины отрезка по основанию 2
- int logN;
- //значения, хранящиеся в таблице
- vector<vector<element>> buffer;
- //логарифм по основанию 2 с округлением вниз
- //например log(10)=log(8)=3
- int log(const int val) const {
- if (val == 1) return 0;
- return log(val / 2) + 1;
- }
- public:
- sparce_table(vector<element>& data) : N(data.size()) {
- //инициализация таблицы:
- //шаг 0: подготовка
- logN = log(N);
- buffer.assign(N, vector<element>());
- //шаг 1: если отрезок состоит из 1 числа - оно и есть минимальный элемент
- for (int i = 0; i < N; i++) {
- buffer[i].push_back(data[i]);
- }
- //шаг2: мы уже знаем минимальные элементы для первой и второй половин отрезка
- // наименьший из них будет наименьшим для всего отрезка
- //сначала рассматриваем отрезки длины 1, потом 2 и т.д.
- for (int j = 1; j < logN + 1; j++) {
- //i-индекс начала отрезка
- for (int i = 0; i < N; i++) {
- //индекс конца отрезка (включительно)
- int right_border_id = i + (1 << j) - 1;
- //конец отрезка выходит за границу массива
- if (right_border_id + 1 > N) break;
- //середина отрезка
- int middle_id = i + (1 << (j - 1));
- //смотрим на 2 подотрезка:
- //(от начала до середины) и (от середины до конца)
- //buffer[i][j] = ...
- buffer[i].push_back(min(buffer[i][j - 1], buffer[middle_id][j - 1]));
- }
- }
- }
- //получить данные, хранящиеся в таблице
- const vector<vector<element>>& get_buffer() const {
- return buffer;
- }
- };
- class RMQ {
- private:
- //бесконечность
- const int INF = 0x80000000 - 1;
- //Sparse Table
- const vector<vector<element>>& ST;;
- //логарифм по основанию 2 с округлением вниз
- //например log(10)=log(8)=3
- int log(const int val) const {
- if (val == 1) return 0;
- return log(val / 2) + 1;
- }
- public:
- RMQ(int M, const sparce_table& _Table) : ST(_Table.get_buffer()) {}
- //минимум на отрезке с индексами от L до R включительно
- element find_min(const int L, const int R) const {
- assert(R >= L);
- //if (R == L) return ST[L][0];
- int j = log(R - L + 1);
- return min(ST[L][j], ST[R - (1 << j) + 1][j]);
- }
- int size() const { return ST.size(); }
- };
- class Graph {
- private:
- int v_num;
- //список смежности
- vector<vector<int>> buffer;
- void add_edge(const int from, const int to) { buffer[from].push_back(to); }
- //ввод графа с потока
- friend void operator>>(istream& inp, Graph& G) {
- for (int i = 1; i < G.size(); i++) {
- int parent;
- inp >> parent;
- G.add_edge(parent, i);
- }
- }
- public:
- //создание и считывание графа
- Graph(int _v_num) : v_num(_v_num) {
- buffer.resize(v_num);
- cin >> *this;
- }
- int size() const { return v_num; }
- //обходим граф в глубину:
- //-попадая в вершину(в том числе и в корень)
- //--записываем ее в конец массива way(изначально пустого)
- //---мы записываем вершину в массив, даже возвращаясь в нее из рекурсии
- //--также, первый раз посетив вершину мы запоминаем
- //---под каким индексом она была записана в way
- void dfs(vector<int>& pos, vector<element>& way,
- const int cur_num, const int cur_depth) const {
- //первый раз посетили вершину
- //запоминаем ее
- way.push_back(element(cur_num, cur_depth));
- //и ее индекс
- pos[cur_num] = way.size() - 1;
- //рекурсивно запускаем dfs от детей
- for (auto _next: buffer[cur_num]){
- dfs(pos, way, _next, cur_depth + 1);
- //При каждом посещении вершины, записываем ее в way
- way.push_back(element(cur_num, cur_depth));
- }
- }
- };
- //поиск наименьшего общего предка 2 вершин(решение задачи)
- class LCA {
- private:
- //параметры для вычисления a3, a4 и т.д.
- long long x, y, z;
- //вершины, для которых ищут LCA
- long long a1, a2;
- //количество вершин в дереве
- long long N;
- //количество запросов
- int M;
- friend void operator>>(istream& inp, LCA& lca) {
- inp >> lca.a1 >> lca.a2;
- inp >> lca.x >> lca.y >> lca.z;
- }
- //найти следующую вершину для запроса
- //-например, найти a3, зная a1 и a2
- int next_v() const { return (a1 * x + a2 * y + z) % N; }
- //перейти к следующему запросу
- //-например, заменить a1 и a2 на a3 и a4
- void next_request() {
- //заменить a1 на a3
- a1 = next_v();
- //поменять местами a3 и a2
- swap(a1, a2);
- //заменить a2 на a4
- a1 = next_v();
- //поменять местами a4 и a3
- swap(a1, a2);
- }
- public:
- //решение задачи(конструктор)
- LCA(int _M, const Graph& G): M(_M), N(G.size()) {
- cin >> *this;
- //pos[i] - индекс (первого вхождения) вершины i в массиве way
- vector<int> pos(N, -1);
- //очередность обхода dfs-ом
- vector<element> way;
- G.dfs(pos, way, 0, 0);
- //помещаем 'хронологию' обхода в sapce table
- sparce_table table(way);
- //и готовимся искать минимум на выбранном участке
- RMQ rmq(M, table);
- //ans - ответ на предыдущий запрос
- //у самого первого запроса не было предыдущего, так что для него ans.num = 0
- element ans(0, -1);
- //сумма всех ответов на запросы(ответ на задачу)
- int s = 0;
- //для каждого из M запросов
- for (int i = 0; i < M; i++) {
- //выбираем участок 'хронологии' в зависимости от запроса
- int first_pos = pos[(a1 + ans.num) % N];
- int second_pos = pos[a2];
- //левый индекс должен быть меньше правого
- if (first_pos > second_pos) { swap(first_pos, second_pos); }
- //ищем минимум на интересующем нас участке
- ans = rmq.find_min(first_pos, second_pos);
- //прибавляем к сумме результат запроса
- s += ans.num;
- //генерируем следующий запрос
- next_request();
- }
- //вывод ответа
- cout << s;
- }
- };
- int main()
- {
- int N, M;
- cin >> N >> M;
- Graph G(N);
- LCA(M, G);
- system("pause");
- return 0;
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement