#include <bits/stdc++.h>
using namespace std;
template <typename T>
void chkmax(T &x, T y) {
  x = x > y ? x : y;
}
template <typename T>
void chkmin(T &x, T y) {
  x = x > y ? y : x;
}
template <typename T>
void update(T &x, T y, T mod) {
  x = x + y > mod ? x + y - mod : x + y;
}
template <typename T>
void multi(T &x, T y, T mod) {
  x = 1ll * x * y % mod;
}
const int INF = (1ll << 30);
template <typename T>
void read(T &x) {
  x = 0;
  bool f = 1;
  char ch;
  do {
    ch = getchar();
    if (ch == '-') f = 0;
  } while (ch > '9' || ch < '0');
  do {
    x = x * 10 + ch - '0';
    ch = getchar();
  } while (ch >= '0' && ch <= '9');
  x = f ? x : -x;
}
template <typename T>
void write(T x) {
  if (x < 0) x = ~x + 1, putchar('-');
  if (x > 9) write(x / 10);
  putchar(x % 10 + '0');
}
const int N = 1000 + 7;
const int M = 2e5 + 7;
namespace LCT {
struct LCT {
  int sz, fa, ch[2];
  bool rev;
} t[M];
inline bool ntroot(int p) {
  return t[t[p].fa].ch[0] == p || t[t[p].fa].ch[1] == p;
}
inline void pushup(int p) {
  t[p].sz = t[(t[p].ch[0])].sz + t[(t[p].ch[1])].sz + 1;
}
inline void pushtag(int p) {
  swap((t[p].ch[0]), (t[p].ch[1]));
  t[p].rev ^= 1;
}
inline void pushdown(int p) {
  if (t[p].rev) {
    if ((t[p].ch[0])) pushtag((t[p].ch[0]));
    if ((t[p].ch[1])) pushtag((t[p].ch[1]));
    t[p].rev ^= 1;
  }
}
inline void pushall(int p) {
  if (ntroot(p)) pushall(t[p].fa);
  pushdown(p);
}
inline void rotate(int x) {
  int y = t[x].fa, z = t[y].fa, k = t[y].ch[1] == x;
  if (ntroot(y)) t[z].ch[t[z].ch[1] == y] = x;
  t[x].fa = z;
  t[y].ch[k] = t[x].ch[k ^ 1];
  if (t[x].ch[k ^ 1]) t[t[x].ch[k ^ 1]].fa = y;
  t[y].fa = x;
  t[x].ch[k ^ 1] = y;
  pushup(y);
  pushup(x);
}
inline void splay(int p) {
  pushall(p);
  while (ntroot(p)) {
    int f = t[p].fa, g = t[f].fa;
    if (ntroot(f)) rotate((t[f].ch[0] == p) == (t[g].ch[0] == f) ? f : p);
    rotate(p);
  }
}
inline void access(int x) {
  for (int y = 0; x; y = x, x = t[x].fa) {
    splay(x);
    t[x].ch[1] = y;
    pushup(x);
  }
}
inline void makeroot(int p) {
  access(p);
  splay(p);
  pushtag(p);
}
inline int findroot(int p) {
  access(p);
  splay(p);
  while ((t[p].ch[0])) {
    pushdown(p);
    p = (t[p].ch[0]);
  }
  splay(p);
  return p;
}
inline void split(int x, int y) {
  makeroot(x);
  access(y);
  splay(y);
}
inline bool link(int x, int y) {
  makeroot(x);
  if (findroot(y) == x) return false;
  t[x].fa = y;
  return true;
}
inline bool cut(int x, int y) {
  makeroot(x);
  if (findroot(y) != x || t[y].fa != x || (t[y].ch[0])) return false;
  t[y].fa = (t[x].ch[1]) = 0;
  pushup(x);
  return true;
}
}  // namespace LCT
namespace Segment_Tree {
struct Node {
  int mn, cnt;
};
Node operator+(Node a, Node b) {
  Node c;
  c = (Node){0, 0};
  c.mn = min(a.mn, b.mn);
  if (c.mn == a.mn) c.cnt += a.cnt;
  if (c.mn == b.mn) c.cnt += b.cnt;
  return c;
}
struct Segment_Tree {
  int l, r, add;
  Node x;
} t[N * N];
inline void pushdown(int p) {
  t[(p << 1)].add += t[p].add;
  t[(p << 1 | 1)].add += t[p].add;
  t[(p << 1)].x.mn += t[p].add;
  t[(p << 1 | 1)].x.mn += t[p].add;
  t[p].add = 0;
}
inline void build(int p, int l, int r) {
  t[p].l = l;
  t[p].r = r;
  t[p].x.cnt = t[p].r - t[p].l + 1;
  if (l == r) return;
  int mid = (t[p].l + t[p].r) >> 1;
  build((p << 1), l, mid);
  build((p << 1 | 1), mid + 1, r);
}
inline void modify(int p, int l, int r, int k) {
  if (l <= t[p].l && t[p].r <= r) {
    t[p].x.mn += k;
    t[p].add += k;
    return;
  }
  pushdown(p);
  int mid = (t[p].l + t[p].r) >> 1;
  if (l <= mid) modify((p << 1), l, r, k);
  if (r > mid) modify((p << 1 | 1), l, r, k);
  t[p].x = t[(p << 1)].x + t[(p << 1 | 1)].x;
}
inline Node query(int p, int l, int r) {
  if (l <= t[p].l && t[p].r <= r) return t[p].x;
  pushdown(p);
  int mid = (t[p].l + t[p].r) >> 1;
  if (r <= mid) return query((p << 1), l, r);
  if (l > mid) return query((p << 1 | 1), l, r);
  Node a = query((p << 1), l, r), b = query((p << 1 | 1), l, r);
  return a + b;
}
}  // namespace Segment_Tree
int n, m, val[N][N];
int dx[] = {0, 1, -1, 0};
int dy[] = {1, 0, 0, -1};
vector<int> vec, esuf[M], epre[M];
int main() {
  read(n);
  read(m);
  for (int i = 1; i <= n; i++)
    for (int j = 1; j <= m; j++) read(val[i][j]);
  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 || x > n || y < 1 || y > m) continue;
        if (val[x][y] > val[i][j]) esuf[val[i][j]].push_back(val[x][y]);
        if (val[x][y] < val[i][j]) epre[val[i][j]].push_back(val[x][y]);
      }
    }
  }
  Segment_Tree::build(1, 1, n * m);
  int r = 0;
  long long ans = 0;
  for (int l = 1; l <= n * m; l++) {
    bool flag = false;
    for (int r1 = r + 1; r1 <= n * m; r1++) {
      vec.clear();
      for (int j = 0; j < epre[r1].size(); j++) {
        int now = epre[r1][j];
        if (now < l) continue;
        if (!LCT::link(r1, now)) {
          flag = true;
          break;
        }
        vec.push_back(now);
      }
      if (flag) {
        for (int j = 0; j < vec.size(); j++) LCT::cut(r1, vec[j]);
        break;
      }
      ++r;
      int tot = 0;
      for (int j = 0; j < epre[r1].size(); j++) {
        if (epre[r1][j] < l) continue;
        LCT::link(r1, epre[r1][j]);
        tot++;
      }
      Segment_Tree::modify(1, r, r, r - l + 1);
      Segment_Tree::modify(1, r, n * m, -tot);
    }
    Segment_Tree::Node t = Segment_Tree::query(1, l, r);
    if (t.mn == 1) ans += t.cnt;
    for (int j = 0; j < esuf[l].size(); j++) {
      if (esuf[l][j] > r) continue;
      LCT::cut(l, esuf[l][j]);
      Segment_Tree::modify(1, esuf[l][j], n * m, 1);
    }
    Segment_Tree::modify(1, l, r, -1);
  }
  printf("%lld\n", ans);
  return 0;
}
