Оскільки між будь-якою парою міст існує рівно один шлях, дорожня мережа країни утворює дерево. Щоб мінімізувати загальні витрати, Чіпу та Дейлу вигідно зустрітися якомога раніше. Це станеться, якщо вони зустрінуться у своєму найближчому спільному предку, після чого зможуть разом поїхати на таксі до столиці, де знаходиться кінотеатр.
Реалізація алгоритму
Для знаходження найближчого спільного предка (LCA) використовується метод двійкового підйому. Граф зберігається у вигляді списку суміжності в масиві . Також оголошуються необхідні глобальні масиви для роботи алгоритму LCA:
— час входу у вершину при пошуку в глибину;
— час виходу з вершини при пошуку в глибину;
dist[v] — відстань від кореня до вершини ;
up[v][k] — зберігає 2^k-го предка вершини ;
#define MAX 100010
#define LOGMAX 18
vector<int> g[MAX];
int timer, d[MAX], f[MAX], dist[MAX];
int up[MAX][LOGMAX];Функція dfs виконує обхід дерева в глибину, починаючи з вершини . Вершина є батьком вершини . Відстань від столиці (вершини з номером ) до вершини дорівнює ; це значення зберігається в масиві dist[v]. У процесі обходу також обчислюються мітки часу входу та виходу з вершини — та .
void dfs (int v, int p = 1, int len = 0)
{
d[v] = timer++;Зберігаємо безпосереднього батька вершини .
up[v][0] = p; Зберігаємо відстань від кореня (столиці) до вершини .
dist[v] = len;Будуємо таблицю двійкових предків.
for (int i = 1; i <= l; i++)
up[v][i] = up[up[v][i-1]][i-1];Із вершини розглядаються всі можливі переходи до сусідніх вершин. Для кожної вершини , з'єднаної з ребром дерева (v, to), виконується перехід.
for (int to : g[v])
if (to != p) dfs (to, v, len + 1);
f[v] = timer++;
}Функція Parent повертає істину, якщо вершина є предком вершини .
int Parent(int a, int b)
{
return (d[a] <= d[b]) && (f[a] >= f[b]);
}Функція LCA обчислює найближчого спільного предка (LCA) двох вершин та у дереві за допомогою методу двійкового підйому та таблиці предків .
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,&Price);Обчислюємо значення l = ⌈log_2n⌉.
l = 1;
while ((1 << l) <= n) l++;Будуємо неорієнтований граф, який за умовою задачі є деревом.
for(i = 0; i < n - 1; i++)
{
scanf("%d %d",&x,&y);
g[x].push_back(y);
g[y].push_back(x);
}Запускаємо на дереві пошук в глибину, починаючи зі столиці, де знаходиться кінотеатр.
dfs(1);Послідовно обробляємо запитів.
scanf("%d",&q);
for(i = 0; i < q; i++)
{
scanf("%d %d",&x,&y);
lca = LCA(x, y);До зустрічі Чіп проїде dist[x] - dist[lca] км, а Дейл dist[y] - dist[lca] км. Разом на таксі вони проїдуть рівно dist[lca] км.
Chip = dist[x] - dist[lca];
Deil = dist[y] - dist[lca];Обчислюємо та виводимо підсумкові витрати.
printf("%.5lf %.5lf\n", (Chip + dist[lca] / 2.0) * Price,
(Deil + dist[lca] / 2.0) * Price);
}