#include <bits/stdc++.h>
using namespace std;
ifstream fin("radiatie.in");
ofstream fout("radiatie.out");
const int N = 15005;
const int M = 2*N;
const int D = 15;
const int infinite = 1<<30;
int totalNodes, totalEdges, totalQuestions;
int root[N], parent[N], normalized[N], costs[N],
firstAppearance[N], lastAppearance[N], answers[N],
deepFirstSearchChart[N], dynamic[N];
int rangeMaximum[D][N];
int arbore[4*M];
int contor, queryLeft, queryRight;
tuple <int, int, int> edges[M]; // cost, origin, destination
vector <int> nodes[N];
vector <int> eulerChart;
vector <pair <int, int> > questions[N];
bitset <M> used;
int getRoot(int node){
if ( node == root[node] )
return node;
root[node] = getRoot(root[node]);
return root[node];
}
int commonAncestor(int position = 1, int left = 1, int right = eulerChart.size()){
if ( left > queryRight or right < queryLeft )
return infinite;
if ( queryLeft <= left and right <= queryRight )
return arbore[position];
int middle = (left+right)/2;
return min( commonAncestor(2*position, left, middle), commonAncestor(2*position+1, middle+1, right) );
}
int getMax(int first, int second){
if ( first < second )
return -infinite;
int currentDepth = log2(first - second + 1);
return max(rangeMaximum[currentDepth][deepFirstSearchChart[first]], rangeMaximum[currentDepth][deepFirstSearchChart[second+(1<<currentDepth)-1]]);
}
inline void readVariables(){
fin >> totalNodes >> totalEdges >> totalQuestions;
int origin, destination, cost;
for ( int index = 1; index <= totalEdges; index++ ){
fin >> origin >> destination >> cost;
edges[index] = make_tuple(cost, origin, destination);
}
}
inline void initializeRangeMaximum(){
for ( int depth = 1; depth < D; depth++ )
for ( int index = 1; index <= totalNodes; index++ )
rangeMaximum[depth][index] = -infinite;
costs[1] = -infinite;
}
inline void partiallyArbore(){
for ( int index = 1; index <= totalNodes; index++ )
root[index] = index;
sort(edges+1, edges+totalEdges+1);
int origin, destination, cost;
for ( int index = 1; index <= totalEdges; index++ ){
tie(cost, origin, destination) = edges[index];
int rootOrigin = getRoot(origin),
rootDestination = getRoot(destination);
if ( rootDestination != rootOrigin ){
nodes[origin].push_back(destination);
nodes[destination].push_back(origin);
used[index] = true;
root[rootOrigin] = root[rootDestination];
}
}
}
void liniarize(int currentNode = 1, int source = 0){
contor++;
parent[currentNode] = source;
normalized[currentNode] = contor;
for ( auto it : nodes[currentNode] )
if ( it != source )
liniarize(it, currentNode);
}
inline void determinateCosts(){
int origin, destination, cost;
for ( int index = 1; index <= totalEdges; index++ ){
if ( used[index] ){
tie(cost, origin, destination) = edges[index];
if ( parent[destination] == origin )
costs[destination] = cost;
else
costs[origin] = cost;
}
}
}
void eulerSearch(int currentNode = 1, int source = 0){
int index = normalized[currentNode];
eulerChart.push_back(index);
firstAppearance[index] = eulerChart.size();
for ( auto it : nodes[currentNode] ){
if ( it != source ){
eulerSearch(it, currentNode);
eulerChart.push_back(index);
}
}
lastAppearance[index] = eulerChart.size();
}
void buildIntervals(int position = 1, int left = 1, int right = eulerChart.size()){
if ( left == right ){
arbore[position] = eulerChart[left-1];
return;
}
int middle = (left+right)/2;
buildIntervals(2*position, left, middle);
buildIntervals(2*position+1, middle+1, right);
arbore[position] = min(arbore[2*position], arbore[2*position+1]);
}
inline void readQuery(){
int firstNode, secondNode;
for ( int index = 1; index <= totalQuestions; index++ ){
answers[index] = -infinite;
fin >> firstNode >> secondNode;
firstNode = normalized[firstNode];
secondNode = normalized[secondNode];
if ( firstAppearance[firstNode] > lastAppearance[secondNode] )
swap(firstNode, secondNode);
queryLeft = firstAppearance[firstNode];
queryRight = lastAppearance[secondNode];
int lowestCommonAncestor = commonAncestor();
questions[firstNode].push_back({lowestCommonAncestor, index});
questions[secondNode].push_back({lowestCommonAncestor, index});
}
}
void deepFirstSearch(int currentNode = 1, int source = 0, int depth = 1){
int index = normalized[currentNode];
deepFirstSearchChart[depth] = index;
dynamic[index] = depth;
rangeMaximum[0][index] = costs[currentNode];
for ( int indexDepth = 1; (1<<indexDepth) <= depth; indexDepth++ )
rangeMaximum[indexDepth][index] = max(rangeMaximum[indexDepth-1][index], rangeMaximum[indexDepth-1][deepFirstSearchChart[depth-(1<<(indexDepth-1))]]);
for ( auto it : questions[index] ){
int aux = getMax(dynamic[index], dynamic[it.first] + 1);
answers[it.second] = max(answers[it.second], aux);
}
for ( auto it : nodes[currentNode] )
if ( it != source )
deepFirstSearch(it, currentNode, depth+1);
}
inline void printSolution(){
for ( int index = 1; index <= totalQuestions; index++ )
fout << answers[index] << "\n";
}
inline void debuggingPurposes(){
for ( auto it : eulerChart )
cout << it << " ";
cout << "\n\n";
for ( int index = 1; index <= totalNodes*4; index++ )
cout << arbore[index] << " ";
cout << "\n\n";
for ( int index = 1; index <= totalNodes; index++ )
cout << dynamic[index] << " ";
}
int main(){
readVariables();
initializeRangeMaximum();
partiallyArbore();
liniarize();
determinateCosts();
eulerSearch();
buildIntervals();
readQuery();
deepFirstSearch();
printSolution();
return 0;
}