#include <bits/stdc++.h>
using namespace std;
const int mx = 3e5 + 5, di[4] = {-1, 0, 1, 0}, dj[4] = {0, -1, 0, 1};
int n, m, q, id, linei, linej, par[mx * 4], dep[mx], up[mx * 4][21], val[mx * 4];
string A[mx]; queue<pair<int, int>> bfsQ; vector<int> dist[mx], adj[mx * 4]; vector<array<int, 3>> edges;
int enc(int i, int j, bool crossParity){
// Note that nodes are 1 indexed
return 1 + i * m + j + crossParity * n * m;
}
bool inGrid(int i, int j){
return i >= 0 and i < n and j >= 0 and j < m;
}
int getR(int i){
return i == par[i] ? i : par[i] = getR(par[i]);
}
void merge(int a, int b, int w){
a = getR(a); b = getR(b);
if (a == b)
return;
adj[id].push_back(a);
adj[id].push_back(b);
val[id] = w;
par[a] = par[b] = id;
id++;
}
void dfs(int i){
for (int l = 1; l < 21; l++)
up[i][l] = up[up[i][l - 1]][l - 1];
for (int to : adj[i]){
if (to != up[i][0]){
up[to][0] = i;
dep[to] = dep[i] + 1;
dfs(to);
}
}
}
int qry(int x, int y){
if (dep[x] < dep[y])
swap(x, y);
for (int l = 20, jmp = dep[x] - dep[y]; ~l; l--){
if (jmp & (1 << l)){
x = up[x][l];
}
}
if (x == y)
return val[x];
for (int l = 20; ~l; l--){
if (up[x][l] != up[y][l]){
x = up[x][l];
y = up[y][l];
}
}
return val[up[x][0]];
}
int main(){
ios_base::sync_with_stdio(0); cin.tie(0);
cin >> n >> m >> q;
for (int i = 0; i < n; i++){
cin >> A[i];
for (int j = 0; j < m; j++){
dist[i].push_back(1e9);
if (A[i][j] == 'v'){
dist[i][j] = 0;
bfsQ.push({i, j});
}
if (A[i][j] == '#'){
linei = i;
linej = j;
}
}
}
// Multisource BFS to find min distance to volcano
while (bfsQ.size()){
auto [i, j] = bfsQ.front(); bfsQ.pop();
for (int dir = 0; dir < 4; dir++){
int ni = i + di[dir], nj = j + dj[dir];
if (inGrid(ni, nj) and dist[i][j] + 1 < dist[ni][nj]){
dist[ni][nj] = dist[i][j] + 1;
bfsQ.push({ni, nj});
}
}
}
// Get the edges
for (int i = 0; i < n; i++){
for (int j = 0; j < m; j++){
// Look at cells to the up and left (so dir = 0 and dir = 1)
for (int dir = 0; dir < 2; dir++){
int ni = i + di[dir], nj = j + dj[dir];
if (inGrid(ni, nj) and A[i][j] != '#' and A[ni][nj] != '#'){
int w = min(dist[i][j], dist[ni][nj]);
// Crosses the line
if (i == linei and ni == linei - 1 and j > linej){
edges.push_back({w, enc(i, j, 0), enc(ni, nj, 1)});
edges.push_back({w, enc(i, j, 1), enc(ni, nj, 0)});
}
// Doesn't cross the line
else{
edges.push_back({w, enc(i, j, 0), enc(ni, nj, 0)});
edges.push_back({w, enc(i, j, 1), enc(ni, nj, 1)});
}
}
}
}
}
// We merge from largest w to smallest
sort(edges.begin(), edges.end(), greater<array<int, 3>>());
// Init DSU stuff
id = n * m * 2 + 1;
iota(par, par + mx * 4, 0);
// Merge
for (auto [w, u, v] : edges)
merge(u, v, w);
// DFS to construct the Kruskal's reconstruction trees
for (int i = n * m * 4; i; i--)
if (!up[i][0])
dfs(i);
// Answer queries via LCA queries
for (int i = 0; i < q; i++){
int x, y; cin >> x >> y;
x--; y--;
cout<<qry(enc(x, y, 0), enc(x, y, 1))<<"\n";
}
}