#include <bits/stdc++.h>
using namespace std;
namespace zyt {
template <typename T>
inline bool read(T &x) {
  char c;
  bool f = false;
  x = 0;
  do c = getchar();
  while (c != EOF && c != '-' && !isdigit(c));
  if (c == EOF) return false;
  if (c == '-') f = true, c = getchar();
  do x = x * 10 + c - '0', c = getchar();
  while (isdigit(c));
  if (f) x = -x;
  return true;
}
template <typename T>
inline void write(T x) {
  static char buf[20];
  char *pos = buf;
  if (x < 0) putchar('-'), x = -x;
  do *pos++ = x % 10 + '0';
  while (x /= 10);
  while (pos > buf) putchar(*--pos);
}
const int N = 1e3 + 10, P = 2e5 + 10;
class Link_Cut_Tree {
 private:
  struct nulltag {};
  struct node {
    bool revtag;
    node *fa, *s[2];
    node(nulltag) : revtag(false) { fa = s[0] = s[1] = this; }
    node() : revtag(false) { fa = s[0] = s[1] = null; }
  } * pos[P];
  static node *null;
  static void update(node *const rot) {}
  static void rev(node *const rot) {
    swap(rot->s[0], rot->s[1]);
    rot->revtag ^= 1;
  }
  static void pushdown(node *const rot) {
    if (rot->revtag) {
      if (rot->s[0] != null) rev(rot->s[0]);
      if (rot->s[1] != null) rev(rot->s[1]);
      rot->revtag = false;
    }
  }
  static void pushdownall(node *const rot) {
    if (!isrot(rot)) pushdownall(rot->fa);
    pushdown(rot);
  }
  static bool dir(const node *const rot) { return rot->fa->s[1] == rot; }
  static bool isrot(const node *const rot) {
    return rot->fa->s[0] != rot && rot->fa->s[1] != rot;
  }
  static void rotate(node *const rot) {
    node *f = rot->fa, *ff = f->fa;
    bool d = dir(rot);
    if (!isrot(f)) ff->s[dir(f)] = rot;
    rot->fa = ff;
    f->s[d] = rot->s[!d];
    if (rot->s[!d] != null) rot->s[!d]->fa = f;
    rot->s[!d] = f;
    f->fa = rot;
    update(f);
  }
  static void splay(node *const rot) {
    pushdownall(rot);
    while (!isrot(rot)) {
      node *f = rot->fa;
      if (isrot(f))
        rotate(rot);
      else if (dir(rot) ^ dir(f))
        rotate(rot), rotate(rot);
      else
        rotate(f), rotate(rot);
    }
    update(rot);
  }
  static void access(node *rot) {
    for (node *last = null; rot != null; last = rot, rot = rot->fa)
      splay(rot), rot->s[1] = last, update(rot);
  }
  static void mkrot(node *const rot) { access(rot), splay(rot), rev(rot); }
  static node *findrot(node *rot) {
    access(rot), splay(rot);
    pushdown(rot);
    while (rot->s[0] != null) pushdown(rot = rot->s[0]);
    splay(rot);
    return rot;
  }

 public:
  void build(const int n) {
    for (int i = 1; i <= n; i++) pos[i] = new node();
  }
  bool connect(const int x, const int y) {
    return findrot(pos[x]) == findrot(pos[y]);
  }
  void link(const int x, const int y) {
    node *rot1 = pos[x], *rot2 = pos[y];
    mkrot(rot2), rot2->fa = rot1;
  }
  void cut(const int x, const int y) {
    node *rot1 = pos[x], *rot2 = pos[y];
    mkrot(rot1), access(rot2), splay(rot2);
    rot2->s[0] = rot1->fa = null;
    update(rot2);
  }
} lct;
Link_Cut_Tree::node *Link_Cut_Tree::null =
    new Link_Cut_Tree::node(Link_Cut_Tree::nulltag());
namespace Segment_Tree {
struct node {
  int min, num, tag;
} tree[P * 4];
void update(node &a, const node &b, const node &c) {
  if (b.min < c.min)
    a = b, a.tag = 0;
  else if (b.min > c.min)
    a = c, a.tag = 0;
  else
    a = (node){b.min, b.num + c.num, 0};
}
void update(const int rot) {
  update(tree[rot], tree[rot << 1], tree[rot << 1 | 1]);
}
void add(const int rot, const int x) { tree[rot].min += x, tree[rot].tag += x; }
void pushdown(const int rot) {
  if (tree[rot].tag) {
    add(rot << 1, tree[rot].tag);
    add(rot << 1 | 1, tree[rot].tag);
    tree[rot].tag = 0;
  }
}
void build(const int rot, const int lt, const int rt) {
  if (lt == rt) {
    tree[rot].min = -lt, tree[rot].num = 1;
    return;
  }
  int mid = (lt + rt) >> 1;
  build(rot << 1, lt, mid);
  build(rot << 1 | 1, mid + 1, rt);
  update(rot);
}
void add(const int rot, const int lt, const int rt, const int ls, const int rs,
         const int x) {
  if (ls <= lt && rt <= rs) {
    add(rot, x);
    return;
  }
  int mid = (lt + rt) >> 1;
  pushdown(rot);
  if (ls <= mid) add(rot << 1, lt, mid, ls, rs, x);
  if (rs > mid) add(rot << 1 | 1, mid + 1, rt, ls, rs, x);
  update(rot);
}
node query(const int rot, const int lt, const int rt, const int ls,
           const int rs) {
  if (ls <= lt && rt <= rs) return tree[rot];
  int mid = (lt + rt) >> 1;
  pushdown(rot);
  if (rs <= mid)
    return query(rot << 1, lt, mid, ls, rs);
  else if (ls > mid)
    return query(rot << 1 | 1, mid + 1, rt, ls, rs);
  else {
    node a, b, c;
    a = query(rot << 1, lt, mid, ls, rs);
    b = query(rot << 1 | 1, mid + 1, rt, ls, rs);
    update(c, a, b);
    return c;
  }
}
}  // namespace Segment_Tree
const int dx[] = {1, -1, 0, 0};
const int dy[] = {0, 0, 1, -1};
int n, m, p, arr[N][N];
pair<int, int> pos[P];
long long ans;
int work() {
  using namespace Segment_Tree;
  read(n), read(m), p = n * m;
  for (int i = 1; i <= n; i++)
    for (int j = 1; j <= m; j++)
      read(arr[i][j]), pos[arr[i][j]] = pair<int, int>(i, j);
  lct.build(p);
  int l = 1;
  build(1, 1, p);
  for (int r = 1; r <= p; r++) {
    add(1, 1, p, 1, p, 1);
    for (int d = 0; d < 4; d++) {
      int x = dx[d] + pos[r].first, y = dy[d] + pos[r].second;
      if (x > 0 && x <= n && y > 0 && y <= m && arr[x][y] < r) {
        while (arr[x][y] >= l && lct.connect(r, arr[x][y])) {
          for (int d = 0; d < 4; d++) {
            int x = dx[d] + pos[l].first, y = dy[d] + pos[l].second;
            if (x > 0 && x <= n && y > 0 && y <= m && arr[x][y] > l &&
                arr[x][y] < r) {
              lct.cut(l, arr[x][y]);
              add(1, 1, p, 1, l, 1);
            }
          }
          ++l;
        }
        if (arr[x][y] >= l) {
          lct.link(r, arr[x][y]);
          add(1, 1, p, 1, arr[x][y], -1);
        }
      }
    }
    node res = query(1, 1, p, l, r);
    ans += (res.min == 0 ? res.num : 0);
  }
  write(ans);
  return 0;
}
}  // namespace zyt
int main() { return zyt::work(); }
