#include <bits/stdc++.h>
using namespace std;
long long gcd(long long a, long long b) { return b == 0 ? a : gcd(b, a % b); }
long long perf[100];
struct BalancedBinaryForest {
  int n;
  vector<bool> isblack;
  vector<int> blackheight;
  vector<int> lft, rgt, par;
  void init(int _n) {
    n = _n;
    isblack = vector<bool>(n, true);
    blackheight = vector<int>(n, 1);
    lft = rgt = par = vector<int>(n, -1);
  }
  pair<int, int> split(int a) {
    ++perf[0];
    int l = lft[a], r = rgt[a];
    lft[a] = rgt[a] = -1;
    if (l != -1) par[l] = -1;
    if (r != -1) par[r] = -1;
    if (l != -1 && !isblack[l]) isblack[l] = true, ++blackheight[l];
    if (r != -1 && !isblack[r]) isblack[r] = true, ++blackheight[r];
    while (par[a] != -1) {
      ++perf[1];
      int p = par[a], pl = lft[p], pr = rgt[p];
      lft[p] = rgt[p] = -1;
      par[a] = par[p];
      par[p] = -1;
      if (par[a] != -1) (lft[par[a]] == p ? lft[par[a]] : rgt[par[a]]) = a;
      if (pl != a && pl != -1) par[pl] = -1;
      if (pr != a && pr != -1) par[pr] = -1;
      if (pl != a && pl != -1 && !isblack[pl])
        isblack[pl] = true, ++blackheight[pl];
      if (pr != a && pr != -1 && !isblack[pr])
        isblack[pr] = true, ++blackheight[pr];
      if (a == pl) r = join3(r, p, pr);
      if (a == pr) l = join3(pl, p, l);
    }
    isblack[a] = true;
    blackheight[a] = 1;
    return make_pair(l, r);
  }
  int rotatelft(int a) {
    ++perf[2];
    assert(par[a] == -1);
    int c = rgt[a], d = lft[c];
    rgt[a] = d;
    if (d != -1) par[d] = a;
    lft[c] = a;
    par[a] = c;
    par[c] = -1;
    return c;
  }
  int rotatergt(int c) {
    ++perf[3];
    assert(par[c] == -1);
    int a = lft[c], d = rgt[a];
    lft[c] = d;
    if (d != -1) par[d] = c;
    rgt[a] = c;
    par[c] = a;
    par[a] = -1;
    return a;
  }
  int height(int x) { return x == -1 ? 0 : blackheight[x]; }
  int joinlft(int a, int x, int b) {
    ++perf[4];
    if (height(a) == height(b) && (a == -1 || isblack[a])) {
      isblack[x] = false;
      blackheight[x] = height(a);
      lft[x] = a;
      rgt[x] = b;
      if (a != -1) par[a] = x;
      if (b != -1) par[b] = x;
      return x;
    }
    int r = rgt[a];
    if (r != -1) par[r] = -1;
    rgt[a] = joinlft(r, x, b);
    par[rgt[a]] = a;
    if (!isblack[a] && !isblack[rgt[a]]) {
      isblack[a] = true;
      ++blackheight[a];
      return a;
    }
    if (isblack[a] && isblack[rgt[a]] &&
        blackheight[a] == blackheight[rgt[a]]) {
      assert(!isblack[rgt[rgt[a]]]);
      a = rotatelft(a);
      isblack[a] = false;
      isblack[rgt[a]] = true;
      ++blackheight[rgt[a]];
      return a;
    }
    return a;
  }
  int joinrgt(int a, int x, int b) {
    ++perf[5];
    if (height(a) == height(b) && (b == -1 || isblack[b])) {
      isblack[x] = false;
      blackheight[x] = height(a);
      lft[x] = a;
      rgt[x] = b;
      if (a != -1) par[a] = x;
      if (b != -1) par[b] = x;
      return x;
    }
    int l = lft[b];
    if (l != -1) par[l] = -1;
    lft[b] = joinrgt(a, x, l);
    par[lft[b]] = b;
    if (!isblack[b] && !isblack[lft[b]]) {
      isblack[b] = true;
      ++blackheight[b];
      return b;
    }
    if (isblack[b] && isblack[lft[b]] &&
        blackheight[b] == blackheight[lft[b]]) {
      assert(!isblack[lft[lft[b]]]);
      b = rotatergt(b);
      isblack[b] = false;
      isblack[lft[b]] = true;
      ++blackheight[lft[b]];
      return b;
    }
    return b;
  }
  int join3(int a, int x, int b) {
    ++perf[6];
    int ret = height(a) >= height(b) ? joinlft(a, x, b) : joinrgt(a, x, b);
    if (!isblack[ret]) isblack[ret] = true, ++blackheight[ret];
    return ret;
  }
  int join2(int a, int b) {
    assert(a == -1 && b == -1 || a != b);
    ++perf[7];
    if (a == -1)
      return b;
    else if (b == -1)
      return a;
    int c = lft[b], d = rgt[b];
    lft[b] = rgt[b] = -1;
    if (c != -1) par[c] = -1;
    if (d != -1) par[d] = -1;
    if (c != -1 && !isblack[c]) isblack[c] = true, ++blackheight[c];
    if (d != -1 && !isblack[d]) isblack[d] = true, ++blackheight[d];
    return join3(join2(a, c), b, d);
  }
  int merge(int a, int b) {
    ++perf[8];
    int ret = join2(getroot(a), getroot(b));
    perf[11] = max(perf[11], (long long)height(ret));
    return ret;
  }
  int getroot(int a) {
    ++perf[9];
    while (a != -1 && par[a] != -1) ++perf[10], a = par[a];
    return a;
  }
  void myassert(bool cond, const char *s, int who) {
    if (cond) return;
    printf("fail: %s for %d\n", s, who + 1);
    assert(cond);
  }
  void verify() {
    for (int i = (0); i < (n); ++i)
      if (par[i] != -1)
        myassert(lft[par[i]] == i || rgt[par[i]] == i,
                 "parent with incorrect child", i);
    for (int i = (0); i < (n); ++i)
      if (lft[i] != -1) myassert(par[lft[i]] == i, "lft incorrect parent", i);
    for (int i = (0); i < (n); ++i)
      if (rgt[i] != -1) myassert(par[rgt[i]] == i, "rgt incorrect parent", i);
    for (int i = (0); i < (n); ++i)
      if (par[i] != -1 && !isblack[i])
        myassert(isblack[par[i]], "red with red parent", i);
    for (int i = (0); i < (n); ++i)
      myassert(height(lft[i]) == height(rgt[i]), "diff heights", i);
    for (int i = (0); i < (n); ++i)
      myassert(height(i) == height(lft[i]) + (isblack[i] ? 1 : 0),
               "incorrect height", i);
  }
};
struct ForestDynamicConnectivity {
  int n;
  BalancedBinaryForest bbf;
  queue<int> pool;
  void init(int _n) {
    n = _n;
    bbf.init(n + 2 * (n - 1));
    for (int i = (n); i < (bbf.n); ++i) pool.push(i);
  }
  int getnode() {
    assert(!pool.empty());
    int ret = pool.front();
    pool.pop();
    return ret;
  }
  void releasenode(int x) { pool.push(x); }
  void reroot(int a) {
    int b, c;
    tie(b, c) = bbf.split(a);
    bbf.merge(a, bbf.merge(c, b));
  }
  pair<int, int> addedge(int a, int b) {
    reroot(a);
    reroot(b);
    int ab = getnode(), ba = getnode();
    bbf.merge(bbf.merge(a, ab), bbf.merge(b, ba));
    return make_pair(ab, ba);
  }
  void remedge(pair<int, int> e) {
    int a, b, c;
    tie(a, b) = bbf.split(e.first);
    if (bbf.getroot(a) == bbf.getroot(e.second)) {
      c = b;
      tie(a, b) = bbf.split(e.second);
    } else {
      tie(b, c) = bbf.split(e.second);
    }
    releasenode(e.first), releasenode(e.second);
    bbf.merge(a, c);
  }
  int getrepresentative(int a) { return bbf.getroot(a); }
};
struct SegTreeMinCounter {
  int n;
  vector<int> smn, smncnt, slazy;
  void sapply(int x, int by) {
    smn[x] += by;
    slazy[x] += by;
  }
  void spush(int x) {
    if (slazy[x] != 0)
      sapply(2 * x + 1, slazy[x]), sapply(2 * x + 2, slazy[x]), slazy[x] = 0;
  }
  void spull(int x) {
    smn[x] = min(smn[2 * x + 1], smn[2 * x + 2]);
    smncnt[x] = (smn[2 * x + 1] == smn[x] ? smncnt[2 * x + 1] : 0) +
                (smn[2 * x + 2] == smn[x] ? smncnt[2 * x + 2] : 0);
  }
  void sinit(int x, int l, int r) {
    slazy[x] = 0;
    if (l == r) {
      smn[x] = 0, smncnt[x] = 1;
    } else {
      int m = l + (r - l) / 2;
      sinit(2 * x + 1, l, m);
      sinit(2 * x + 2, m + 1, r);
      spull(x);
    }
  }
  void smod(int x, int l, int r, int L, int R, int BY) {
    if (L <= l && r <= R) {
      sapply(x, BY);
    } else {
      int m = l + (r - l) / 2;
      spush(x);
      if (L <= m) smod(2 * x + 1, l, m, L, R, BY);
      if (m + 1 <= R) smod(2 * x + 2, m + 1, r, L, R, BY);
      spull(x);
    }
  }
  int sget(int x, int l, int r, int L, int R, int VAL) {
    if (L <= l && r <= R) {
      assert(smn[x] >= VAL);
      return smn[x] == VAL ? smncnt[x] : 0;
    } else {
      int m = l + (r - l) / 2;
      spush(x);
      int ret = 0;
      if (L <= m) ret += sget(2 * x + 1, l, m, L, R, VAL);
      if (m + 1 <= R) ret += sget(2 * x + 2, m + 1, r, L, R, VAL);
      return ret;
    }
  }
  void init(int _n) {
    n = _n;
    smn = smncnt = slazy = vector<int>(4 * n);
    sinit(0, 0, n - 1);
  }
  void mod(int l, int r, int by) { smod(0, 0, n - 1, l, r, by); }
  int get(int l, int r, int val) { return sget(0, 0, n - 1, l, r, val); }
};
const int MAXH = 1000;
const int MAXW = 1000;
const int MAXN = 200000;
const int DX[] = {-1, 0, +1, 0}, DY[] = {0, +1, 0, -1};
int h, w, n;
int g[MAXH][MAXW];
pair<int, int> pos[MAXN];
ForestDynamicConnectivity conn;
vector<pair<int, int>> connedges[MAXN];
SegTreeMinCounter segtree;
long long solve() {
  for (int x = (0); x < (h); ++x)
    for (int y = (0); y < (w); ++y) pos[g[x][y]] = make_pair(x, y);
  conn.init(n);
  segtree.init(n);
  long long ret = 0;
  int l = 0;
  for (int r = (0); r < (n); ++r) {
    int rx = pos[r].first, ry = pos[r].second;
    while (true) {
      vector<int> adj;
      for (int k = (0); k < (4); ++k) {
        int nx = rx + DX[k], ny = ry + DY[k];
        if (nx < 0 || nx >= h || ny < 0 || ny >= w || g[nx][ny] < l ||
            g[nx][ny] > r)
          continue;
        adj.push_back(conn.getrepresentative(g[nx][ny]));
      }
      sort(adj.begin(), adj.end());
      bool duplicate = false;
      for (int i = (1); i < (((int)(adj).size())); ++i)
        if (adj[i - 1] == adj[i]) duplicate = true;
      if (!duplicate) break;
      for (int i = (0); i < (((int)(connedges[l]).size())); ++i)
        conn.remedge(connedges[l][i]);
      ++l;
    }
    segtree.mod(0, r, +1);
    for (int k = (0); k < (4); ++k) {
      int nx = rx + DX[k], ny = ry + DY[k];
      if (nx < 0 || nx >= h || ny < 0 || ny >= w || g[nx][ny] < l ||
          g[nx][ny] > r)
        continue;
      connedges[g[nx][ny]].push_back(conn.addedge(g[nx][ny], r));
      segtree.mod(0, g[nx][ny], -1);
    }
    int cur = segtree.get(l, r, 1);
    ret += cur;
  }
  return ret;
}
void run() {
  scanf("%d%d", &h, &w);
  n = h * w;
  for (int x = (0); x < (h); ++x)
    for (int y = (0); y < (w); ++y) scanf("%d", &g[x][y]), --g[x][y];
  printf("%lld\n", solve());
}
void stress() {
  std::mt19937 rnd(1237891);
  for (int rep = (0); rep < (1); ++rep) {
    h = 992, w = 191, n = h * w;
    memset(g, -1, sizeof(g));
    int at = 0;
    for (int xx = (0); xx < (h); ++xx) {
      int x = (2 * xx) % h + (2 * xx >= h ? 1 : 0);
      for (int y = (0); y < (w); ++y) g[x][y] = at++;
    }
    for (int x = (0); x < (h); ++x)
      for (int y = (0); y < (w); ++y) assert(g[x][y] != -1);
    long long have = solve();
    printf("%lld\n", have);
  }
  for (int i = (0); i <= (11); ++i) printf("perf[%d]=%lld\n", i, perf[i]);
}
int main() {
  run();
  return 0;
}
