Poiché tra ogni coppia di città esiste esattamente un percorso, la rete stradale del paese forma un albero. Per minimizzare le spese totali, conviene a Chip e Dale incontrarsi il prima possibile. Questo accadrà se si incontrano nel loro antenato comune più vicino, dopo di che potranno insieme prendere un taxi per la capitale, dove si trova il cinema.
Implementazione dell'algoritmo
Per trovare l'antenato comune più vicino (LCA) si utilizza il metodo del sollevamento binario (binary lifting). Il grafo è memorizzato come lista di adiacenza nell'array . Vengono inoltre dichiarati gli array globali necessari per il funzionamento dell'algoritmo LCA:
— tempo di ingresso nel vertice durante la ricerca in profondità;
— tempo di uscita dal vertice durante la ricerca in profondità;
dist[v] — distanza dalla radice al vertice ;
up[v][k] — memorizza il 2^k-esimo antenato del vertice ;
#define MAX 100010
#define LOGMAX 18
vector<int> g[MAX];
int timer, d[MAX], f[MAX], dist[MAX];
int up[MAX][LOGMAX];La funzione dfs esegue una visita in profondità dell'albero, a partire dal vertice . Il vertice è il genitore del vertice . La distanza dalla capitale (vertice numero ) al vertice è pari a ; questo valore viene salvato nell'array dist[v]. Durante la visita vengono inoltre calcolate le etichette di tempo di ingresso e uscita dal vertice — e .
void dfs (int v, int p = 1, int len = 0)
{
d[v] = timer++;Salviamo il genitore diretto del vertice .
up[v][0] = p; Salviamo la distanza dalla radice (la capitale) al vertice .
dist[v] = len;Costruiamo la tabella degli antenati binari.
for (int i = 1; i <= l; i++)
up[v][i] = up[up[v][i-1]][i-1];Dal vertice vengono considerati tutti i possibili passaggi ai vertici adiacenti. Per ogni vertice , collegato a da un arco dell'albero (v, to), viene effettuato il passaggio.
for (int to : g[v])
if (to != p) dfs (to, v, len + 1);
f[v] = timer++;
}La funzione Parent restituisce vero se il vertice è un antenato del vertice .
int Parent(int a, int b)
{
return (d[a] <= d[b]) && (f[a] >= f[b]);
}La funzione LCA calcola l'antenato comune più vicino (LCA) di due vertici e nell'albero utilizzando il metodo del sollevamento binario e la tabella degli antenati .
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];
}Parte principale del programma. Leggiamo i dati di input.
scanf("%d %d",&n,&Price);Calcoliamo il valore l = ⌈log_2n⌉.
l = 1;
while ((1 << l) <= n) l++;Costruiamo il grafo non orientato, che secondo le condizioni del problema rappresenta un albero.
for(i = 0; i < n - 1; i++)
{
scanf("%d %d",&x,&y);
g[x].push_back(y);
g[y].push_back(x);
}Avviamo sull'albero la ricerca in profondità, a partire dalla capitale dove si trova il cinema.
dfs(1);Elaboriamo in sequenza richieste.
scanf("%d",&q);
for(i = 0; i < q; i++)
{
scanf("%d %d",&x,&y);
lca = LCA(x, y);Prima dell'incontro Chip percorrerà dist[x] - dist[lca] km, e Dale dist[y] - dist[lca] km. Insieme in taxi percorreranno esattamente dist[lca] km.
Chip = dist[x] - dist[lca];
Deil = dist[y] - dist[lca];Calcoliamo e stampiamo le spese finali.
printf("%.5lf %.5lf\n", (Chip + dist[lca] / 2.0) * Price,
(Deil + dist[lca] / 2.0) * Price);
}