#include <bits/stdc++.h>
#pragma GCC optimize("-ffloat-store")
using namespace std;
int n, m, k, w, x;
vector<pair<int, pair<int, int> > > edges;
vector<string> dt[1002];
vector<int> ke[1002];
int par[1002];
int anc(int p) {
  if (par[p] == p)
    return p;
  else
    return par[p] = anc(par[p]);
}
void join(int p, int q) { par[anc(p)] = anc(q); }
int cntDif(vector<string> &v1, vector<string> &v2) {
  int ans = 0;
  for (long long i = 0; i <= n - 1; i++) {
    for (long long j = 0; j <= m - 1; j++) {
      if (v1[i][j] != v2[i][j]) ans++;
    }
  }
  return ans * w;
}
int stand(int &vl) {
  if (vl == k + 1) return 0;
  return vl;
}
void dfs(int x, int p) {
  int sz = ke[x].size();
  for (int i = 0; i < sz; i++) {
    int v = ke[x][i];
    if (v != p) {
      cout << stand(v) << " " << stand(x) << "\n";
      dfs(v, x);
    }
  }
}
int main() {
  ios_base::sync_with_stdio(0);
  cin.tie(NULL);
  cout.tie(NULL);
  cin >> n >> m >> k >> w;
  string s;
  for (long long ind = 1; ind <= k; ind++) {
    for (long long i = 1; i <= n; i++) {
      cin >> s;
      dt[ind].push_back(s);
    }
  }
  for (long long i = 1; i <= k; i++) {
    for (long long j = i + 1; j <= k; j++) {
      int w = cntDif(dt[i], dt[j]);
      edges.push_back({w, {i, j}});
    }
    edges.push_back({m * n, {k + 1, i}});
  }
  priority_queue<pair<int, pair<int, int> >,
                 std::vector<pair<int, pair<int, int> > >,
                 std::greater<pair<int, pair<int, int> > > >
      edges_PQ(edges.begin(), edges.end());
  int cost = 0;
  for (int i = 1; i <= k + 1; i++) par[i] = i;
  int cnt = 0;
  while (edges_PQ.size()) {
    pair<int, pair<int, int> > e = edges_PQ.top();
    edges_PQ.pop();
    if (anc(e.second.first) != anc(e.second.second)) {
      cnt++;
      join(e.second.first, e.second.second);
      cost += e.first;
      ke[e.second.first].push_back(e.second.second);
      ke[e.second.second].push_back(e.second.first);
      if (cnt == k) break;
    }
  }
  cout << cost << "\n";
  dfs(k + 1, k + 1);
}
