#include <bits/stdc++.h>
using namespace std;
struct edge {
  int x;
  int y;
  int cost;
};
bool visited[1001] = {false};
vector<edge> output;
vector<vector<int> > output2;
bool sortByCost(const edge &e1, const edge &e2) {
  if (e1.cost == e2.cost) return e1.y < e2.y;
  if (e1.cost == e2.cost && e1.y == e2.y) return e1.x < e2.x;
  return e1.cost < e2.cost;
}
bool sortByY(const edge &e1, const edge &e2) { return e1.y < e2.y; }
class Disjoint_Set {
  int p[1001];

 public:
  int n;
  Disjoint_Set(int x) {
    n = x;
    createSet(x);
  }
  void createSet(int x) {
    for (int i = 0; i <= x; i++) {
      p[i] = i;
    }
  }
  void mergeSet(int a, int b) {
    int pa = findSet(a);
    int pb = findSet(b);
    if (pa != pb) {
      p[pb] = pa;
    }
  }
  int findSet(int a) {
    if (p[a] == a) return a;
    return p[a] = findSet(p[a]);
  }
  vector<int> getMainParents() {
    vector<int> v;
    for (int i = 1; i <= n; i++) {
      if (findSet(i) == i) {
        v.push_back(findSet(i));
      }
    }
    return v;
  }
};
void dfs(int cr) {
  if (visited[cr]) return;
  visited[cr] = true;
  for (int i = 0; i < output2[cr].size(); i++) {
    if (visited[output2[cr][i]]) continue;
    cout << output2[cr][i] << ' ' << cr << endl;
    dfs(output2[cr][i]);
  }
}
int main() {
  int n, m, k, w, diff = 0;
  cin >> n >> m >> k >> w;
  char arrChars[k][n][m];
  bool taken[k + 1];
  for (int i = 0; i < k; i++) {
    for (int j = 0; j < n; j++) {
      for (int s = 0; s < m; s++) {
        cin >> arrChars[i][j][s];
      }
    }
    output2.push_back(vector<int>(0));
  }
  output2.push_back(vector<int>(0));
  vector<edge> v;
  int nm = n * m;
  edge e;
  int tmpCost = 0;
  e.x = 1;
  e.y = 0;
  e.cost = 0;
  v.push_back(e);
  for (int i = 1; i < k; i++) {
    e.x = i + 1;
    e.y = 0;
    e.cost = nm;
    v.push_back(e);
  }
  for (int i = 0; i < k; i++) {
    for (int j = i + 1; j < k; j++) {
      e.x = i + 1;
      e.y = j + 1;
      tmpCost = 0;
      for (int s = 0; s < n; s++) {
        for (int q = 0; q < m; q++) {
          if (arrChars[i][s][q] != arrChars[j][s][q]) tmpCost++;
        }
      }
      e.cost = tmpCost * w;
      v.push_back(e);
    }
  }
  sort(v.begin(), v.end(), sortByCost);
  taken[0] = true;
  Disjoint_Set ds(k + 1);
  int finalCost = 0;
  for (int i = 0; i < v.size(); i++) {
    if (ds.findSet(v[i].x) != ds.findSet(v[i].y)) {
      output2[v[i].x].push_back(v[i].y);
      output2[v[i].y].push_back(v[i].x);
      ds.mergeSet(v[i].x, v[i].y);
      if (!taken[v[i].x]) {
        output.push_back(v[i]);
        taken[v[i].x] = true;
      } else {
        int t = v[i].x;
        v[i].x = v[i].y;
        v[i].y = t;
        output.push_back(v[i]);
        taken[v[i].x] = true;
      }
      finalCost += v[i].cost;
    }
  }
  cout << finalCost + nm << endl;
  dfs(0);
  return 0;
}
