Fuel on Tree Routes
National Olympiad — National round
Submission name: fuel.cpp
Problem summary
N cities form a weighted undirected tree. Each unit of road length consumes one unit of fuel. For each query, report the fuel needed on the unique route between the two named cities.
Input
N Q; N−1 triples u v w; Q vertex pairs.
Output
One route length per query in input order.
Constraints
1≤N≤5000; 1≤Q≤200000; 1≤w≤2000000; memory limit 16 MB. Half the tests have N≤2000,Q≤10000.
Worked example
An independently prepared example for this explanation.
Input
4 3 1 2 4 2 3 7 2 4 2 3 4 1 3 2 2
Output
9 11 0
Solution analysis
- Root the tree at 1 and calculate depth and weighted root distance iteratively.
- Build binary-lifting ancestors to answer lowest-common-ancestor queries.
- For L=LCA(u,v), return dist[u]+dist[v]−2·dist[L].
Why it works
The two root routes share exactly the route from root to L. Subtracting its length twice leaves precisely the unique u–v path. Binary lifting raises vertices without skipping their lowest common ancestor.
Watch out. Weighted distances require 64-bit integers; avoid an N² distance matrix.
Commented C++17 solution
An independently written complete program with standard headers.
// Key idea: The two root routes share exactly the route from root to L. Subtracting its length twice
// leaves precisely the unique u–v path.
#include<iostream>
#include<vector>
#include<algorithm>
#include<utility>
#include<tuple>
using namespace std;
int main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n,q;
cin>>n>>q;
vector<vector<pair<int,int>>>g(n+1);
for(int i=1;i<n;i++){
int u,v,w;
cin>>u>>v>>w;
g[u].push_back({v,w});
g[v].push_back({u,w});
}
int L=1;
while((1<<L)<=n){
L++;
}
vector<vector<int>>up(L,vector<int>(n+1));
vector<int>dep(n+1),order{1};
vector<long long>d(n+1);
for(size_t i=0;i<order.size();i++){
int u=order[i];
for(auto[v,w]:g[u]){
if(v!=up[0][u]){
up[0][v]=u;
dep[v]=dep[u]+1;
d[v]=d[u]+w;
order.push_back(v);
}
}
}
for(int k=1;k<L;k++){
for(int v=1;v<=n;v++){
up[k][v]=up[k-1][up[k-1][v]];
}
}
auto lca=[&](int a,int b){
if(dep[a]<dep[b]){
swap(a,b);
}
int diff=dep[a]-dep[b];
for(int k=0;k<L;k++){
if(diff>>k&1){
a=up[k][a];
}
}
if(a==b){
return a;
}
for(int k=L-1;k>=0;k--){
if(up[k][a]!=up[k][b]){
a=up[k][a];
b=up[k][b];
}
}
return up[0][a];
};
while(q--){
int a,b;
cin>>a>>b;
cout<<d[a]+d[b]-2*d[lca(a,b)]<<'\n';
}
}