#include <iostream>

using namespace std;

typedef struct{
	int color;
	bool is_checked;
} building_t;

inline int xy(const int _x, const int _y, const int _W){ return _x + _y * (_W + 2); }
inline int xLeftOblique(const int _x, const int _y){ return _x - (_y+1)%2; }
inline int xRightOblique(const int _x, const int _y){ return _x + _y%2; }

bool isSurrounded(building_t *_buildings, int *_index, int *_memo, const int _x, const int _y, const int _W){
	int coordinate = xy(_x, _y, _W);
	building_t tmp = _buildings[coordinate];

	// 建物があるなら true を返す
	if(tmp.color == 1) return true;

	// 地図外なら false を返す
	if(tmp.color == -1) return false;

	// 既に調べていたら true を返す
	if(tmp.is_checked) return true;

	// チェック済みであることをメモ
	_buildings[coordinate].is_checked = true;

	// 隣接マスを調べる（再帰呼び出し）
	// 奇数行と偶数行で隣接の座標の規則が異なるので注意
	const int xlo = xLeftOblique(_x, _y);
	const int xro = xRightOblique(_x, _y);

	// -> false が返ってきたら 地図外(-1)にして false を返す
	if(!isSurrounded(_buildings, _index, _memo, xlo,  _y-1, _W)){ _buildings[coordinate].color = -1; return false; }	// 左上
	if(!isSurrounded(_buildings, _index, _memo, xro,  _y-1, _W)){ _buildings[coordinate].color = -1; return false; }	// 右上
	if(!isSurrounded(_buildings, _index, _memo, _x-1, _y,   _W)){ _buildings[coordinate].color = -1; return false; }	// 左
	if(!isSurrounded(_buildings, _index, _memo, _x+1, _y,   _W)){ _buildings[coordinate].color = -1; return false; }	// 右
	if(!isSurrounded(_buildings, _index, _memo, xlo,  _y+1, _W)){ _buildings[coordinate].color = -1; return false; }	// 左下
	if(!isSurrounded(_buildings, _index, _memo, xro,  _y+1, _W)){ _buildings[coordinate].color = -1; return false; }	// 右下

	// すべてをパスしたら囲まれている可能性があるので true を返し, color を 1 にする
	// 囲まれいない場合を考慮して調べたマスをメモしておく -> 囲まれていなかったら後でまとめて color を -1 にする
	_buildings[coordinate].color = 1;
	_memo[*_index] = coordinate;
	(*_index)++;
	return true;
}

int main(){
	int W, H;
	cin >> W >> H;

	// 地図外のマスを余分に用意しておく <- isSurround を再帰的に呼び出すので地図の外に飛び出す可能性あるため
	const int W2 = W + 2;
	const int H2 = H + 2;

	building_t *buildings = new building_t[W2 * H2];

	// 建物の配置を入力
	// 地図外 -> -1, 建物なし(白) -> 0, 建物あり(灰色) -> 1
	for(int y=0; y<H2; y++){
		for(int x=0; x<W2; x++){
			const int coordinate = xy(x, y, W);
			if(x == 0 || x == W2 - 1 || y == 0 || y == H2 - 1) buildings[coordinate].color = -1;
			else cin >> buildings[coordinate].color;
			buildings[coordinate].is_checked = false;
		}
	}

	// 囲まれているところを調べる -> color を囲まれていたら 1, 囲まれていなかったら -1 に変える
	int *memo = new int[W*H];	// 一時的に調べたマスをメモしておく -> 囲まれていなかったらまとめて color を -1 にする
	for(int y=1; y<=H; y++){
		for(int x=1; x<=W; x++){
			int index = 0;	// メモの引数
			if(!isSurrounded(buildings, &index, memo, x, y, W)){
				for(int i=0; i<index; i++) buildings[memo[i]].color = -1;
			}
		}
	}

	// 壁面の長さを求める
	int answer = 0;
	for(int y=1; y<=H; y++){
		for(int x=1; x<=W; x++){
			// 建物がある場合は隣接を調べる
			if(buildings[xy(x, y, W)].color == 1){

			// -> 隣接が地図外(color == -1)なら長さ +1
				int xlo = xLeftOblique(x, y);
				int xro = xRightOblique(x, y);
				if(buildings[xy(xlo, y-1, W)].color == -1) answer++;	//左上
				if(buildings[xy(xro, y-1, W)].color == -1) answer++;	//右上
				if(buildings[xy(x-1, y,   W)].color == -1) answer++;	//左
				if(buildings[xy(x+1, y  , W)].color == -1) answer++;	//右
				if(buildings[xy(xlo, y+1, W)].color == -1) answer++;	//左下
				if(buildings[xy(xro, y+1, W)].color == -1) answer++;	//右下
			}
		}
	}

	cout << answer << endl;

	delete[] buildings;
	delete[] memo;
	return 0;
}

Measured memory_bytes: 1780