#include <bits/stdc++.h>
using namespace std;

using lnt = long long;
template <class T> using SET = set<T>;
template <class T, class S> using MAP = map<T, S>;

#define LOOP(n) for (lnt _ = 0; _ < (n); _++)
#define REP(i, n) for (lnt i = 0; i < (n); i++)            // 0, 1, ..., n-1
#define INVREP(i, n) for (lnt i = (n)-1; i >= 0; i--)      // n-1, n-2, ..., 0
#define FOR(i, a, b) for (lnt i = (a); i < (b); i++)       // a, a+1, ..., b-1
#define INVFOR(i, a, b) for (lnt i = (b)-1; i >= (a); i--) // b-1, b-2, ..., a
#define ALL(a) a.begin(), a.end()

const lnt MOD = 1e9 + 7; // AtCoderのMOD
const lnt INF = 1e18;

const vector<lnt> vcDIGIT = {48, 49, 50, 51, 52, 53, 54, 55, 56, 57};
const vector<lnt> vcLARGE = {65, 66, 67, 68, 69, 70, 71, 72, 73,
                             74, 75, 76, 77, 78, 79, 80, 81, 82,
                             83, 84, 85, 86, 87, 88, 89, 90};
const vector<lnt> vcSMALL = {97,  98,  99,  100, 101, 102, 103, 104, 105,
                             106, 107, 108, 109, 110, 111, 112, 113, 114,
                             115, 116, 117, 118, 119, 120, 121, 122};

#define MAXELM max_element
#define MINELM min_element
#define F first
#define S second
#define LB lower_bound
#define UB upper_bound
#define PB push_back
#define EXIT() return 0

// 'int'を一切タイプしない
#define MAIN int main

template <class InputIterator, class T>
lnt inline CNT(const InputIterator &first, const InputIterator &last,
               const T &value) {
  return count(first, last, value);
}

template <class InputIterator>
lnt inline DIST(const InputIterator &first, const InputIterator &last) {
  return distance(first, last);
}

template <class T> lnt inline SIZE(const T &sequence) {
  return (lnt)(sequence.size());
}

template <class T> string inline STR(const T &value) {
  return to_string(value);
}

lnt inline ABS(const lnt &value) { return abs(value); }

lnt inline MAX(const lnt &value1, const lnt &value2) {
  return max(value1, value2);
}

lnt inline MIN(const lnt &value1, const lnt &value2) {
  return min(value1, value2);
}

template <class InputIterator>
lnt inline ACC(const InputIterator &first, const InputIterator &last,
               const lnt &init = 0) {
  return accumulate(first, last, init);
}

template <class InputIterator>
lnt inline SUM(const InputIterator &first, const InputIterator &last) {
  return accumulate(first, last, 0);
}

template <class InputIterator, class BinaryOperation>
lnt inline ACC(const InputIterator &first, const InputIterator last,
               const lnt init, const BinaryOperation binary_op) {
  return accumulate(first, last, init, binary_op);
}

// n以下の素数列挙
vector<lnt> Primes(const lnt n) {
  if (n < 2) {
    return vector<lnt>(0);
  }

  vector<lnt> vcPri, vcTab(n - 1);
  for (lnt i = 2; i < n + 1; i++) {
    vcTab[i - 2] = i;
  }

  const lnt nRoot = sqrt(n);
  while (vcTab[0] <= nRoot) {
    vcPri.push_back(vcTab[0]);
    vcTab.erase(vcTab.begin());
    vector<lnt> vcTab_fil;
    copy_if(vcTab.begin(), vcTab.end(), back_inserter(vcTab_fil),
            [&vcPri](lnt x) { return x % vcPri.back() != 0; });
    vcTab = vcTab_fil;
    if (vcTab.empty()) {
      break;
    }
  }

  vcPri.reserve(vcPri.size() + vcTab.size());
  vcPri.insert(vcPri.end(), vcTab.begin(), vcTab.end());
  return vcPri;
}

const vector<lnt> vcPRIMES1e6 = Primes(1e6);

// 素因数分解:PrimeDecomp(n)
// 2番目の引数にはPrimes(k) (kは十分大 >= floor(sqrt(n))) を入れる
MAP<lnt, lnt> PrimeDecomp(const lnt n,
                          const vector<lnt> &vcPrimesRoot_n = vcPRIMES1e6) {
  if (n < 2) {
    return MAP<lnt, lnt>();
  }

  MAP<lnt, lnt> PrimeDecomp;

  lnt m = n;
  for (lnt p : vcPrimesRoot_n) {
    while (m % p == 0) {
      m /= p;
      if (PrimeDecomp.count(p)) {
        PrimeDecomp[p]++;
      } else {
        PrimeDecomp.emplace(p, 1);
      }
    }
  }
  if (m != 1) {
    PrimeDecomp.emplace(m, 1);
  }

  return PrimeDecomp;
}

// 約数列挙:Divisors(n)
// 2番目の引数にはPrimes(k) (kは十分大 >= floor(sqrt(n))) を入れる
vector<lnt> Divisors(const lnt n,
                     const vector<lnt> &vcPrimesRoot_n = vcPRIMES1e6) {
  vector<lnt> vcDiv(1, 1), vcTable;
  auto mpPD = PrimeDecomp(n, vcPrimesRoot_n);
  for (auto itrPD = mpPD.begin(); itrPD != mpPD.end(); itrPD++) {
    lnt nPri = itrPD->first, nMulti = itrPD->second;
    vcTable.resize(vcDiv.size() * (nMulti + 1));
    for (lnt i = 0; i < nMulti + 1; i++) {
      transform(vcDiv.begin(), vcDiv.end(),
                vcTable.begin() + (i * vcDiv.size()),
                [nPri, i](lnt x) { return x * pow(nPri, i); });
    }
    vcDiv = vcTable;
  }
  sort(vcDiv.begin(), vcDiv.end());
  return vcDiv;
}

// mod2乘
lnt inline SQ(const lnt n, const lnt mod = MOD) {
  return ((n % mod) * (n % mod)) % mod;
}

// mod冪乗
lnt POW(const lnt x, const lnt y, const lnt mod = MOD) {
  if (x >= 0 and y >= 0) {
    if (y == 0) {
      return 1 % mod;
    }
    if (y == 1) {
      return x % mod;
    }
    if (y % 2 == 0) {
      return SQ(POW(x, y / 2, mod), mod);
    }
    return SQ(POW(x, y / 2, mod), mod) * (x % mod) % mod;
  }
  cout << "Warning!" << endl;
  return 0;
}

// mod 2の冪
lnt inline BIT(const lnt i) {
  if (i >= 0) {
    return POW(2, i);
  }
  cout << "Warning!" << endl;
  return 0;
}

// long long まで許すSQ
lnt inline LSQ(const lnt n) { return n * n; }

// long long まで許すPOW
lnt LPOW(const lnt x, const lnt y) {
  if (x >= 0 and y >= 0) {
    if (y == 0) {
      return 1;
    }
    if (y == 1) {
      return x;
    }
    if (y % 2 == 0) {
      return LSQ(LPOW(x, y / 2));
    }
    return LSQ(LPOW(x, y / 2)) * x;
  }
  cout << "Warning!" << endl;
  return 0;
}

// long longまで許すBIT
lnt inline LBIT(const lnt i) {
  if (i >= 0) {
    return LPOW(2, i);
  }
  cout << "Warning!" << endl;
  return 0;
}

// n以下の階乗/mod階乗のリストを生成　
vector<lnt> vcFACT(const lnt n, const lnt mod = MOD) {
  if (n >= 0) {
    vector<lnt> vcFact = {1 % mod};
    lnt fact = 1 % mod;
    FOR(k, 1, n + 1) {
      fact = (fact * (k % mod)) % mod;
      vcFact.PB(fact);
    }
    return vcFact;
  }
  cout << "Warning!" << endl;
  return vector<lnt>(0);
}

// n以下のmod階乗の逆元のリストを生成(mod:素数を想定)
vector<lnt> vcINVFACT(const lnt n, const lnt mod = MOD) {
  vector<lnt> vcInvFact;
  lnt invfact = POW(vcFACT(n, mod).at(n), mod - 2, mod);
  vcInvFact.PB(invfact);
  INVFOR(k, 1, n + 1) {
    invfact = invfact * (k % mod) % mod;
    vcInvFact.PB(invfact);
  }
  reverse(ALL(vcInvFact));
  return vcInvFact;
}

const vector<lnt> vcMODFACT1e6 = vcFACT(1e6, MOD);
const vector<lnt> vcMODINVFACT1e6 = vcINVFACT(1e6, MOD);

// 階乗
// mod > 0．3番目の引数には
// vcFACT(m, mod)を入れる
// (m >= n:十分大)
lnt inline FACT(const lnt n, const lnt mod = MOD,
                const vector<lnt> &vcModFact = vcMODFACT1e6) {
  return vcModFact.at(n);
}

// 順列の数
// mod:素数を想定．4, 5番目の引数には
// vcFACT(m1, mod)，vcINVFACT(m2, mod)を入れる
// (m1 >= n, m2 >= n-k :十分大)
lnt PERM(const lnt n, const lnt k, const lnt mod = MOD,
         const vector<lnt> &vcModFact = vcMODFACT1e6,
         const vector<lnt> &vcModInvFact = vcMODINVFACT1e6) {
  if (k >= 0) {
    return vcModFact.at(n) * vcModInvFact.at(n - k) % mod;
  }
  cout << "Warning!" << endl;
  cout << "Note that 4th, 5th argument : vcFact(m1, mod), vcFact(m2, mod)."
       << endl;
  return 0;
}

//二項係数
// mod:素数なら，4, 5番目の引数には
// vcFACT(m1, mod)，vcINVFACT(m2, mod)を入れる
// (m1 >= n, m2 >= n-k, k :十分大)
lnt inline COMB(const lnt n, const lnt k, const lnt mod = MOD,
                const vector<lnt> &vcModFact = vcMODFACT1e6,
                const vector<lnt> &vcModInvFact = vcMODINVFACT1e6) {
  return PERM(n, k, mod, vcModFact, vcModInvFact) * vcModInvFact.at(k) % mod;
}

//重複組み合わせの数
// mod:素数なら，4, 5番目の引数には
// vcFACT(m1, mod)，vcINVFACT(m2,mod)を入れる
// (m1 >= n+k-1, m2 >= n-1, k :十分大)
lnt inline HCOMB(const lnt n, const lnt k, const lnt mod = MOD,
                 const vector<lnt> &vcModFact = vcMODFACT1e6,
                 const vector<lnt> &vcModInvFact = vcMODINVFACT1e6) {
  return COMB(n + k - 1, k, mod, vcModFact, vcModInvFact);
}

// 非負整数valueのbase進数表示でのposition桁目を表示
lnt inline MASK(const lnt value, const lnt position, const lnt base = 10) {
  if (value >= 0 and base >= 2) {
    return (value / LPOW(base, position)) % base;
  }
  cout << "WARNING!" << endl;
  return 0;
}

// ビット���スク
lnt inline BMASK(const lnt value, const lnt position) {
  return MASK(value, position, 2);
}

// MASKにおいて，position桁目に数字がない時0ではなくbaseを返す
lnt inline DIGIT(const lnt value, const lnt position, const lnt base = 10) {
  if (value >= 0 and base >= 2) {
    if (value == 0) {
      if (position == 0) {
        return 0;
      }
      return base;
    }
    if (value < LPOW(base, position)) {
      return base;
    }
    return MASK(value, position, base);
  }

  cout << "WARNING!" << endl;
  return 0;
}

// base=2のDIGIT
lnt inline BDIGIT(const lnt value, const lnt position) {
  return DIGIT(value, position, 2);
}

typedef pair<lnt, lnt> P;

// ポテンシャル付きUnion-Find木
template <class T> class UnionFind {
private:
  MAP<T, T> par;
  MAP<T, lnt> rank;
  MAP<T, lnt> diff_from_par;
  lnt sz = 0;
  lnt nTree = 0;
  lnt weight(const T &);

public:
  UnionFind<T>();
  UnionFind<T>(const SET<T> &);
  T find(const T &);
  lnt diff(const T &x, const T &y);
  bool unite(const T &, const T &, const lnt = 0);
  bool equi(const T &x, const T &y) { return find(x) == find(y); }
  lnt size() const { return sz; }
  lnt n_tree() const { return nTree; }
};

template <class T> UnionFind<T>::UnionFind(const SET<T> &st) {
  par.reserve(SIZE(st));
  rank.reserve(SIZE(st));
  diff_from_par.reserve(SIZE(st));
  for (T x : st) {
    par.emplace(x, x);
    rank.emplace(x, 0);
    diff_from_par.emplace(x, 0);
  }
  sz = SIZE(st);
  nTree = SIZE(st);
}

// 経路圧縮しながらrootを返す
template <class T> T UnionFind<T>::find(const T &x) {
  if (par.at(x) == x) {
    return x;
  }
  T root = find(par.at(x));
  diff_from_par.at(x) += diff_from_par.at(par.at(x));
  return par.at(x) = root;
}

// rootを基準としたポテンシャル
template <class T> lnt UnionFind<T>::weight(const T &x) {
  find(x);
  return diff_from_par.at(x);
}

// yを基準としたxのポテンシャルを返す
// 異なる木に対するdiff関数は意味を持たないので，警告も出力
template <class T> lnt UnionFind<T>::diff(const T &x, const T &y) {
  if (not equi(x, y)) {
    cout << "Warning! The roots of x and y are not same." << endl;
  }
  return weight(x) - weight(y);
}

// diff(x, y) = w となるようにxのtreeとyのtreeを繋ぐ
// xのtreeとyのtreeが既に同じで，diff(x, y) ≠ w　なら何もせずにfalseを返す
template <class T>
bool UnionFind<T>::unite(const T &x, const T &y, const lnt w) {
  T p = find(x);
  T q = find(y);
  if (p == q) {
    if (diff(x, y) == w) {
      return true;
    }
    return false;
  }
  lnt v = w + weight(y) - weight(x);
  if (rank.at(p) > rank.at(q)) {
    swap(p, q);
    v = -v;
  }
  if (rank.at(p) == rank.at(q)) {
    rank.at(q)++;
  }
  par.at(p) = q;
  diff_from_par.at(p) = v;
  nTree--;
  return true;
}

// UnionFind<lnt>
template <> class UnionFind<lnt> {
private:
  vector<lnt> par;
  vector<lnt> rank;
  vector<lnt> diff_from_par;
  lnt sz = 0;
  lnt nTree = 0;
  lnt weight(const lnt);

public:
  UnionFind<lnt>();
  UnionFind<lnt>(const SET<lnt> &);
  UnionFind<lnt>(const lnt);
  lnt find(const lnt);
  lnt diff(const lnt x, const lnt y);
  bool unite(const lnt, const lnt, const lnt = 0);
  bool equi(const lnt x, const lnt y) { return find(x) == find(y); }
  lnt size() const { return sz; }
  lnt n_tree() const { return nTree; }
};

UnionFind<lnt>::UnionFind(const SET<lnt> &st) {
  par.reserve(SIZE(st));
  rank = vector<lnt>(SIZE(st), 0);
  diff_from_par = vector<lnt>(SIZE(st), 0);
  for (lnt x : st) {
    par.PB(x);
  }
  sz = SIZE(st);
  nTree = SIZE(st);
}

UnionFind<lnt>::UnionFind(const lnt num) {
  par.reserve(num);
  rank = vector<lnt>(num, 0);
  diff_from_par = vector<lnt>(num, 0);
  REP(i, num) { par.PB(i); }
  sz = num;
  nTree = num;
}

// 経路圧縮しながらrootを返す
lnt UnionFind<lnt>::find(const lnt x) {
  if (par.at(x) == x) {
    return x;
  }
  lnt root = find(par.at(x));
  diff_from_par.at(x) += diff_from_par.at(par.at(x));
  return par.at(x) = root;
}

// rootを基準としたポテンシャル
lnt UnionFind<lnt>::weight(const lnt x) {
  find(x);
  return diff_from_par.at(x);
}

// yを基準としたxのポテンシャルを返す
// 異なる木に対するdiff関数は意味を持たないので，警告も出力
lnt UnionFind<lnt>::diff(const lnt x, const lnt y) {
  if (not equi(x, y)) {
    cout << "Warning! The roots of x and y are not same." << endl;
  }
  return weight(x) - weight(y);
}

// diff(x, y) = w となるようにxのtreeとyのtreeを繋ぐ
// xのtreeとyのtreeが既に同じで，diff(x, y) ≠ w　なら何もせずにfalseを返す
bool UnionFind<lnt>::unite(const lnt x, const lnt y, const lnt w) {
  lnt p = find(x);
  lnt q = find(y);
  if (p == q) {
    if (diff(x, y) == w) {
      return true;
    }
    return false;
  }
  lnt v = w + weight(y) - weight(x);
  if (rank.at(p) > rank.at(q)) {
    swap(p, q);
    v = -v;
  }
  if (rank.at(p) == rank.at(q)) {
    rank.at(q)++;
  }
  par.at(p) = q;
  diff_from_par.at(p) = v;
  nTree--;
  return true;
}

typedef pair<lnt, lnt> P;

MAIN() {
  lnt h, w, n;
  cin >> h >> w >> n;

  const vector<lnt> dk = {-1, 0, 1}, dl = {-1, 0, 1};
  SET<P> stBl, stNrBl;
  LOOP(n) {
    lnt k, l;
    scanf("%lld %lld", &k, &l);
    stBl.emplace(P(k - 1, l - 1));
    REP(i, 3) {
      REP(j, 3) {
        if (1 <= k - 1 + dk.at(i) && k - 1 + dk.at(i) < h - 1 &&
            1 <= l - 1 + dl.at(j) && l - 1 + dl.at(j) < w - 1) {
          stNrBl.emplace(P(k - 1 + dk.at(i), l - 1 + dl.at(j)));
        }
      }
    }
  }

  vector<lnt> vcAns(10, 0);
  vcAns.at(0) = (h - 2) * (w - 2) - SIZE(stNrBl);

  for (P p : stNrBl) {
    lnt cnt = 0;
    REP(i, 3) {
      REP(j, 3) {
        if (stBl.count(P(p.F - dk.at(i), p.S - dl.at(j)))) {
          cnt++;
        }
      }
    }
    vcAns.at(cnt)++;
  }

  REP(num, 10) { printf("%lld\n", vcAns.at(num)); }
}


Measured memory_bytes: 79204