#include <bits/stdc++.h>
using namespace std;
int read() {
  int ans = 0;
  char x = getchar();
  while (!isdigit(x)) x = getchar();
  while (isdigit(x)) ans = ans * 10 + x - '0', x = getchar();
  return ans;
}
int mp[1005][1005], n, m;
pair<int, int> pos[200005];
struct lct {
  struct node {
    int rev, fa, son[2], val, minn;
  } tree[200005];
  int stck[200005], top;
  int ls(int p) { return tree[p].son[0]; }
  int rs(int p) { return tree[p].son[1]; }
  int fa(int p) { return tree[p].fa; }
  int which(int p) {
    if (rs(fa(p)) == p) return 1;
    if (ls(fa(p)) == p) return 0;
    return -1;
  }
  void pushup(int p) {
    tree[p].minn = min(tree[p].val, min(tree[ls(p)].minn, tree[rs(p)].minn));
  }
  void init() {
    tree[0].val = 1e9;
    tree[0].minn = 1e9;
    for (int i = 1; i <= n * m; i++) tree[i].val = i, pushup(i);
  }
  void pushdown(int p) {
    if (tree[p].rev) {
      tree[ls(p)].rev ^= 1;
      tree[rs(p)].rev ^= 1;
      swap(tree[p].son[0], tree[p].son[1]);
      tree[p].rev = 0;
    }
  }
  void rotate(int p) {
    int f1 = fa(p), f2 = fa(f1);
    pushdown(f2);
    pushdown(f1);
    pushdown(p);
    int now1 = which(p), now2 = which(f1);
    tree[f1].son[now1] = tree[p].son[now1 ^ 1];
    tree[tree[p].son[now1 ^ 1]].fa = f1;
    tree[p].son[now1 ^ 1] = f1;
    tree[f1].fa = p;
    tree[p].fa = f2;
    if (now2 != -1) tree[f2].son[now2] = p;
    pushup(f1);
    pushup(p);
    pushup(f2);
  }
  void splay(int p) {
    int pp = p;
    while (which(p) != -1) stck[++top] = p, p = fa(p);
    stck[++top] = p;
    while (top) pushdown(stck[top--]);
    p = pp;
    while (which(p) != -1) {
      int f = fa(p);
      if (which(f) != -1) {
        if (which(f) == which(p))
          rotate(f);
        else
          rotate(p);
      }
      rotate(p);
    }
  }
  void access(int p) {
    int tmp = 0;
    while (p) {
      splay(p);
      tree[p].son[1] = tmp;
      pushup(p);
      tmp = p;
      p = fa(p);
    }
  }
  void makeroot(int p) {
    access(p);
    splay(p);
    tree[p].rev ^= 1;
  }
  int findroot(int p) {
    access(p);
    splay(p);
    pushdown(p);
    while (ls(p)) p = ls(p), pushdown(p);
    splay(p);
    return p;
  }
  void link(int x, int y) {
    makeroot(x);
    if (findroot(y) != x) tree[x].fa = y;
  }
  void cut(int x, int y) {
    makeroot(x);
    if (findroot(y) == x && !ls(y) && tree[x].son[1] == y) {
      tree[x].son[1] = tree[y].fa = 0;
      pushup(x);
    }
  }
} LCT;
struct segmenttree {
  int tree[800005], lazy_tag[800005], cnt[800005];
  int ls(int p) { return p << 1; }
  int rs(int p) { return p << 1 | 1; }
  void pushup(int p) {
    tree[p] = tree[ls(p)];
    cnt[p] = cnt[ls(p)];
    if (tree[rs(p)] < tree[ls(p)]) tree[p] = tree[rs(p)], cnt[p] = 0;
    if (tree[rs(p)] == tree[p]) cnt[p] += cnt[rs(p)];
  }
  void build(int l, int r, int p) {
    if (l == r) {
      tree[p] = 0;
      cnt[p] = 1;
      return;
    }
    int mid = (l + r) >> 1;
    build(l, mid, ls(p));
    build(mid + 1, r, rs(p));
    pushup(p);
  }
  void pushdown(int p) {
    int w = lazy_tag[p];
    lazy_tag[ls(p)] += w;
    lazy_tag[rs(p)] += w;
    tree[ls(p)] += w;
    tree[rs(p)] += w;
    lazy_tag[p] = 0;
  }
  void update(int l, int r, int ll, int rr, int p, int val) {
    if (ll <= l && r <= rr) {
      tree[p] += val;
      lazy_tag[p] += val;
      return;
    }
    int mid = (l + r) >> 1;
    pushdown(p);
    if (ll <= mid) update(l, mid, ll, rr, ls(p), val);
    if (rr > mid) update(mid + 1, r, ll, rr, rs(p), val);
    pushup(p);
  }
  pair<int, int> query(int l, int r, int ll, int rr, int p) {
    if (ll <= l && r <= rr) {
      return make_pair(tree[p], cnt[p]);
    }
    pushdown(p);
    int mid = (l + r) >> 1;
    pair<int, int> ans = make_pair(1e9, 0), tmp1 = make_pair(1e9, 0),
                   tmp2 = make_pair(1e9, 0);
    if (ll <= mid) tmp1 = query(l, mid, ll, rr, ls(p));
    if (rr > mid) tmp2 = query(mid + 1, r, ll, rr, rs(p));
    if (tmp1.first < ans.first) ans.first = tmp1.first, ans.second = 0;
    if (tmp1.first == ans.first) ans.second += tmp1.second;
    if (tmp2.first < ans.first) ans.first = tmp2.first, ans.second = 0;
    if (tmp2.first == ans.first) ans.second += tmp2.second;
    return ans;
  }
} tree;
int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};
vector<int> V[200005];
int main() {
  n = read(), m = read();
  LCT.init();
  for (int i = 1; i <= n; i++)
    for (int j = 1; j <= m; j++) mp[i][j] = read();
  int pos = 0;
  tree.build(1, n * m, 1);
  for (int i = 1; i <= n; i++) {
    for (int j = 1; j <= m; j++) {
      for (int k = 0; k < 4; k++) {
        int x = i + dx[k], y = j + dy[k];
        if (x < 1 || y < 1 || x > n || y > m) continue;
        V[mp[i][j]].push_back(mp[x][y]);
      }
      sort(V[mp[i][j]].begin(), V[mp[i][j]].end());
    }
  }
  long long ans = 0;
  for (int i = 1; i <= n * m; i++) {
    for (int j = pos + 1; j <= n * m; j++) {
      vector<int> tmp;
      for (int k = 0; k < V[j].size(); k++) {
        if (V[j][k] < i || V[j][k] > j) continue;
        tmp.push_back(V[j][k]);
      }
      sort(tmp.begin(), tmp.end());
      bool flag = 0;
      for (int k = 0; k < tmp.size(); k++) {
        LCT.makeroot(j);
        if (LCT.findroot(tmp[k]) == j) {
          flag = 1;
          break;
        }
        LCT.link(tmp[k], j);
      }
      for (int k = 0; k < tmp.size(); k++) {
        LCT.cut(tmp[k], j);
      }
      if (flag) break;
      pos = j;
      int tot = 0;
      for (int k = 0; k < V[j].size(); k++) {
        if (V[j][k] < i || V[j][k] > j) continue;
        tot++;
        LCT.link(j, V[j][k]);
      }
      tree.update(1, n * m, j, j, 1, j - i + 1);
      tree.update(1, n * m, j, n * m, 1, -tot);
    }
    pair<int, int> tmp = tree.query(1, n * m, i, pos, 1);
    if (tmp.first == 1) ans += tmp.second;
    for (int j = 0; j < V[i].size(); j++) {
      if (V[i][j] >= i && V[i][j] <= pos) {
        LCT.cut(i, V[i][j]);
        tree.update(1, n * m, V[i][j], n * m, 1, 1);
      }
    }
    tree.update(1, n * m, i, pos, 1, -1);
  }
  cout << ans;
}
