////////////////////////////////////////////////////////////////////
// This source code is for Visual Studio
////////////////////////////////////////////////////////////////////
#include <iostream>
#include <string>
#include <vector>
#include <list>
#include <queue>
#include <map>
#include <set>
#include <algorithm>
#include <sstream>
#include <iterator>
#include <stack>
#include <functional>
#include <iomanip>
#include <string>
#include <cstring>
#include <deque>
#include <math.h>
#include <assert.h>

#define	numberof(a)	(sizeof(a) / sizeof(a[0]))
#define	INF		UINT64_MAX
#define Rep(i,n) for(int i = 0; i < (n); ++i )

using namespace std;

typedef vector< vector<int> > MAT;
typedef pair<int, int> PINT;
typedef long long ll;
typedef uint32_t u32;
typedef uint64_t u64;
typedef int32_t s32;
typedef int64_t s64;

//constant
//--------------------------------------------
const double EPS = 1e-10;
const double PI  = acos(-1.0);

struct edge
{
	ll from;
	ll to;
	ll cost;

	edge(ll inp_from, ll inp_to, ll inp_cost) {
		from = inp_from;
		to = inp_to;
		cost = inp_cost;
	}

	// コストの大小で順序定義
	bool operator<(const edge& o) const {
		return cost < o.cost;
	}
};

bool edge_comp(const edge& e1, const edge& e2)
{
	return e1.cost < e2.cost;
}

////////////////////
// UnionFind
////////////////////
class UnionFind
{
public:
	vector<int> parent;
	vector<int> rank;

	UnionFind(int n)
	{
		Rep(i, n) {
			parent.push_back(i);
			rank.push_back(1);
		}
	}
	int root(int x)
	{
		if (parent[x] != x) parent[x] = root(parent[x]);
		return parent[x];
	}
	// is same union between x and y
	bool same(int x, int y)
	{
		return root(x) == root(y);
	}
	void unite(int x, int y)
	{
		int rx = root(x);
		int ry = root(y);
		if (rx == ry) return;
		if (rank[rx] > rank[ry]) {
			parent[ry] = rx;
			rank[rx] += rank[ry];
		}
		if (rank[rx] <= rank[ry]) {
			parent[rx] = ry;
			rank[ry] += rank[rx];
		}
	}
};

////////////////////
// kruskal(最小全域木)
////////////////////
vector< edge > shortest_edges;
ll kruskal(vector<edge> & edges, ll V)	// edgesは辺の集合、Vは頂点数
{
	UnionFind uf(V);// Union-Findの初期化
	ll res = 0;

	sort(edges.begin(), edges.end(), edge_comp); // edge.costの小さい順

	for (int i = 0; i < edges.size(); i++)
	{
		edge e = edges[i];
		if (!uf.same(e.from, e.to))
		{
			uf.unite(e.from, e.to);
			res += e.cost;

			// save
			shortest_edges.push_back(e);
		}
	}

	return res;	// 最小の重み
}

////////////////////
// 回文判定 O(N)
////////////////////
bool isPalindrome(string input_str)
{
	bool ret = true;
	ll i = 0;
	ll j = input_str.length() - 1;

	while (ret && i <= j) {
		ret = ret & (input_str[i] == input_str[j]);
		i++;
		j--;
	}
	return ret;
}

////////////////////
// 2分探索 O(logN)
// ※ソートされていることが前提
////////////////////
ll binary_search(ll target, vector<ll> & search_table)
{
	const ll loop_cnt = 1000;	// 要調整
	ll N = search_table.size();
	ll lb = 0;
	ll ub = N;

	for (int i = 0; i < loop_cnt; ++i) {
		ll mid = (lb + ub) / 2;

		if (target == search_table[mid]) {
			return mid;
		}
		else if (target >= search_table[mid]) {
			lb = mid;
		}
		else {
			ub = mid;
		}
	}

	return ub;
}

bool throw_comp(const pair<ll, ll> & p1, const pair<ll, ll> & p2)
{
	// sort(item.begin(), item.end(), throw_comp)のように使う
	return p1.second > p2.second;
}

bool tmp_comp(const pair<ll, ll> & p1, const pair<ll, ll> & p2)
{
	// sort(item.begin(), item.end(), tmp_comp)のように使う
	return ((double)p1.first / p1.second) > ((double)p2.first / p2.second);
}

////////////////////
// しゃくとり法
// 要素数Nの配列A[N]
////////////////////
ll shakutori( ll N, vector<ll> & A )
{
	ll ans = 0;
	ll head = 0;
	ll tail = 0;
	set<ll> memo;

	for (tail = 0; tail < N; tail++) {
		// [head, tail]に同じ数字が2つ以上含まれているときheadを進める
		while (memo.count(A[tail]) != 0) {
			memo.erase(A[head]);
			head++;
		}

		ans = max(ans, tail - head + 1);
		memo.insert(A[tail]);
	}

	return ans;
}

// 数学：		最大公約数、最小公倍数、素因数分解
// 探索：		貪欲法(全探索)、2分探索、ソート
//				BFS(幅優先探索)、DFS(深さ優先探索)
// 探索(発展)：	貪欲法、しゃくとり法、区間累積和
// データ構造：	スタック、キュー、deque
//				ハッシュ、map、リスト
// DP:			桁DP、ビットDP、メモ化再帰
// グラフ:		最短経路(ダイクストラ、ベルマンフォード、ワーシャルフロイト)
//				最小全域木(クラスカル法、プリム法、Union-Find)
//				三角不等式
// その他：		最大フロー、マッチング問題
//				セグメント木、RMQ、BIT、平方分割
// 
//ll dp[500000 * 2000 + 10];	// dp[i日目]までの勝ち数

#define VAL_MAX	(10000000000)
ll solve(ll N, vector<ll> & V)
{
	ll ans = -1;	// when -1, OK
	ll tmp = 0;
	ll cnt0 = 0;
	ll cnt1 = 0;
	bool isOK = false;
	vector<ll> V0(N, VAL_MAX);
	vector<ll> V1(N, VAL_MAX);

	// sort
	Rep(i, N) {
		if (i % 2 == 0) V0[cnt0++] = V[i];
		else V1[cnt1++] = V[i];
	}
	sort(V0.begin(), V0.end());
	sort(V1.begin(), V1.end());
	cnt0 = 0;
	cnt1 = 0;
	Rep(i, N) {
		if (i % 2 == 0) V[i] = V0[cnt0++];
		else V[i] = V1[cnt1++];
	}

	for (ll i = 0; i < N - 1; ++i) {
		if (V[i] > V[i + 1]) {
			ans = i;
			break;
		}
	}

	return ans;
}

int main()
{
	ll H = 0;
	ll W = 0;
	ll D = 0;
	ll Q = 0;
	ll ans = 0;

	cin >> H >> W >> D;
	ll coord_x[305 * 305];
	ll coord_y[305 * 305];
	ll dist_total[305 * 305];// dist_total[i]:iを起点にH * Wまで加算した距離の合計

	Rep(y, H) {
		Rep(x, W) {
			ll tmp = 0;
			cin >> tmp;
			coord_x[tmp] = x;
			coord_y[tmp] = y;
		}
 	}

	cin >> Q;
	vector<ll> L(Q);
	vector<ll> R(Q);
	Rep(i, Q) {
		cin >> L[i] >> R[i];
	}

	// create_table
	for (ll val = D + 1; val <= H*W; val++) {
		dist_total[val] = dist_total[val - D] + abs(coord_x[val] - coord_x[val - D]) + abs(coord_y[val] - coord_y[val - D]);
	}

	// solve
	Rep(i, Q) {
		ans = dist_total[R[i]] - dist_total[L[i]];
		cout << ans << endl;
	}

	return 0;
}


Measured memory_bytes: 4864