//==========Iroha_3856's Library========== //命名規則 //自作関数:ownFunction //自作クラス:OwnClass //#define _GLIBCXX_DEBUG //STL #include using namespace std; //AtCoder-Library #if __has_include() #include using namespace atcoder; using mint = atcoder::modint998244353; #endif //PBDS-tree #if __has_include() #include #include #include using namespace __gnu_pbds; template using Tree = tree, rb_tree_tag, tree_order_statistics_node_update>; //tree.find_by_order(k) = tree[k] (0-indexed) //tree.order_of_key(k) = tree.lower_bound(k) - tree.begin() //Note: Can only be used for non-multiple sets. #endif #define vec vector #define ll long long #define ull unsigned long long #define vi vec #define vll vec #define vb vec #define vvi vec #define vvll vec #define vvb vec #define vvvi vec #define vvvll vec #define vvvb vec #define pii pair #define pll pair #define pli pair #define pq priority_queue template using spq = priority_queue, greater>; #define eb emplace_back #define pb push_back #define all(x) (x).begin(), (x).end() #define rep(i, l, r) for (int i=(int)(l); i<(int)(r); i++) #define REP(i, l, r) for (int i=(int)(l); i<=(int)(r); i++) #define siz(x) (int)x.size() templatebool chmax(T &a, T b) { if (abool chmin(T &a, T b) { if (b vector slice(const vector& A, int l, int r) { assert(0 <= l && l <= r && r <= (int)A.size()); vector ret; for (int i = l; i T allSum(vector A) { return reduce(A.begin(), A.end()); } template T allMax(vector A) { return *max_element(A.begin(), A.end()); } template T allMin(vector A) { return *min_element(A.begin(), A.end()); } template int allArgMax(vector A) { return max_element(A.begin(), A.end())-A.begin(); } template int allArgMin(vector A) { return min_element(A.begin(), A.end())-A.begin(); } ll ceil(ll x, ll y) { assert(y != 0); if ((x >= 0) == (y >= 0)) { return (abs(x) + abs(y)-1)/abs(y); } else { return -(abs(x)/abs(y)); } } ll floor(ll x, ll y) { assert(y != 0); if ((x >= 0) == (y >= 0)) { return abs(x)/abs(y); } else { return -((abs(x) + abs(y)-1)/abs(y)); } } template istream &operator>>(istream &is, pair &p) { is >> p.first >> p.second; return is; } template ostream &operator<<(ostream &os, const pair &p) { os << p.first << " " << p.second; return os; } template istream &operator>>(istream &is, vector &v) { for(auto &x : v) { is >> x; } return is; } template ostream &operator<<(ostream &os, const vector &v) { for(int i = 0; i < (int)v.size(); i++) { if(i != (int)v.size() - 1) os << v[i] << " "; else os << v[i]; } return os; } template void print(T A, bool f = true) { cerr << A; if (f) cerr << endl; } template void print(pair A, bool f = true) { cerr << "{" << A.first << ", " << A.second << "}"; if (f) cerr << endl; } template void print(vector A, bool f = true) { cerr << "{"; for (int i = 0; i<(int)A.size(); i++) { print(A[i], false); if (i+1 != (int)A.size()) cerr << ", "; } cerr << "}"; if (f) cerr << endl; } #define debug(x) cerr << #x << " = "; print(x) struct IoSetup { IoSetup() { cin.tie(nullptr); ios::sync_with_stdio(false); cout << fixed << setprecision(15); cerr << fixed << setprecision(15); } } iosetup; struct Edge { int to; ll cost; Edge(int to, ll cost) : to(to), cost(cost) {} }; const int mod = 998244353; const int MOD = 1000000007; const int inf = 1000010000; const ll INF = 4e18; //グラフ入力 //無向重みなしグラフ vector> graph(int& N, int& M) { cin >> N >> M; vector> G(N); for (int i = 0; i < M; i++) { int u, v; cin >> u >> v; u--; v--; G[u].push_back(v); G[v].push_back(u); } return G; } //無向重み付きグラフ vector> weightedGraph(int& N, int& M) { cin >> N >> M; vector> G(N); for (int i = 0; i < M; i++) { int u, v; ll w; cin >> u >> v >> w; u--; v--; G[u].push_back(Edge{v, w}); G[v].push_back(Edge{u, w}); } return G; } //有向重みなしグラフ vector> directedGraph(int& N, int& M) { cin >> N >> M; vector> G(N); for (int i = 0; i < M; i++) { int u, v; cin >> u >> v; u--; v--; G[u].push_back(v); } return G; } //有向重み付きグラフ vector> weightedDirectedGraph(int& N, int& M) { cin >> N >> M; vector> G(N); for (int i = 0; i < M; i++) { int u, v; ll w; cin >> u >> v >> w; u--; v--; G[u].push_back(Edge{v, w}); } return G; } //1次元累積和 //全て0-indexedで処理 template struct ComulativeSum { int siz; vector S; bool done; ComulativeSum() : ComulativeSum(0) {} ComulativeSum(int N) : ComulativeSum(vector(N, 0)) {} //累積和の構築はしない ComulativeSum(vector A) { done = false; siz = (int)A.size(); S.resize(siz+1); S[0] = 0; for (int i = 0; i < siz; i++) { S[i+1] = A[i]; } } //累積 void run() { assert(!done); for (int i = 1; i <= siz; i++) { S[i] += S[i-1]; } done = true; } //加算(累積前のみ) T add(int idx, T a) { assert(!done); S[idx+1] += a; return S[idx+1]; } //代入(累積前のみ) T set(int idx, T a) { assert(!done); S[idx+1] = a; return S[idx+1]; } //取得(累積前、累積後両方とも可能だが、どちらを想定するかをexpected_doneで渡す) T get(int idx, bool expected_done) { assert(expected_done == done); return S[idx+1]; } //区間和取得(累積後のみ) //半開区間で与える T sum(int l, int r) { assert(done); return S[r]-S[l]; } }; template struct ComulativeSum2D { bool done; int H, W; vector> S; ComulativeSum2D() : ComulativeSum2D(0, 0) {} ComulativeSum2D(int h, int w) : ComulativeSum2D(vector>(h, vector(w))) {} ComulativeSum2D(vector> A) { done = false; H = (int)A.size(); W = (int)A[0].size(); S.resize(H+1, vector(W+1, 0)); for (int i = 0; i < H; i++) { for (int j = 0; j < W; j++) S[i+1][j+1] = A[i][j]; } } //累積 void run() { assert(!done); for (int i = 1; i <= H; i++) { for (int j = 1; j <= W; j++) { S[i][j] += S[i][j-1]; } } for (int j = 1; j <= W; j++) { for (int i = 1; i <= H; i++) S[i][j] += S[i-1][j]; } } //加算(累積前のみ) T add(int r, int c, T x) { assert(!done); S[r+1][c+1] += x; } //代入(累積前のみ) T set(int r, int c, T x) { assert(!done); S[r+1][c+1] = x; } //取得(累積前、累積後両方とも可能だが、どちらを想定するかをexpected_doneで渡す) T get(int r, int c, bool expect_done) { assert(expect_done == done); return S[r+1][c+1]; } //区間和取得(累積後のみ) //行、列とも半開区間で与える T sum(int left_row, int right_row, int left_column, int right_column) { return S[right_row][right_column] - S[right_row][left_column] - S[left_row][right_column] + S[left_row][left_column]; } }; //よくつかうセグ木用の型宣言 //一点代入区間総和 namespace __RangeSumSegTree { template T op(T a, T b) {return a + b;} template T e() {return 0;} }; template using RangeSumSegTree = atcoder::segtree, __RangeSumSegTree::e>; //一点代入区間最小値 namespace __RangeMinSegTree { template T op(T a, T b) {return min(a, b);} template T e() {return __e;} }; template using RangeMinSegTree = atcoder::segtree, __RangeMinSegTree::e>; //一点代入区間最大値 namespace __RangeMaxSegTree { template T op(T a, T b) {return max(a, b);} template T e() {return __e;} }; template using RangeMaxSegTree = atcoder::segtree, __RangeMaxSegTree::e>; //Union-Find struct UnionFind { vector par, siz; int connected_components; UnionFind() { init(0); } //n頂点0辺のグラフを作る UnionFind(int n) { init(n); } void init (int n) { par.resize(n, -1); siz.resize(n, 1); connected_components = n; } //頂点xの連結成分の代表をreturnする int root(int x) { if (par[x]==-1) return x; return par[x] = root(par[x]); } //頂点xと頂点yが同じ連結成分に属しているか判定する bool same(int x, int y) { return root(x)==root(y); } //頂点xと頂点yを結ぶ辺を作る bool merge(int x, int y) { x = root(x); y = root(y); if (x==y) return false; //siz[x] > siz[y]を前提とする if (siz[x] < siz[y]) swap(x, y); par[y] = x; siz[x]+=siz[y]; connected_components--; return true; } //頂点xの連結成分の頂点数をreturnする int size(int x) { return siz[root(x)]; } int connectedComponents() { return connected_components; } //これまでの頂点数をnとすると、頂点nを追加する(0-indexed) //O(1) void add_vertex() { par.push_back(-1); siz.push_back(1); } //これまでの頂点数をnとすると、vector tosであらわされる辺を持った頂点nを追加する (0-indexed) //O(tos.size()) void add_vertex(vector tos) { add_vertex(); int s = (int)par.size(); for (int to : tos) { merge(s-1, to); } } }; //重み付きUnion-Find template struct WeightedUnionFind { vector par, siz; vector diff_weight; WeightedUnionFind() : WeightedUnionFind(0) {} WeightedUnionFind(int n) { init(n); } void init(int n) { par.resize(n, -1); siz.resize(n, 1); diff_weight.resize(n, 0); } int root(int x) { if (par[x]==-1) return x; int r = root(par[x]); diff_weight[x] += diff_weight[par[x]]; return par[x] = r; } T weight(int x) { root(x); return diff_weight[x]; } bool same(int x, int y) { return root(x)==root(y); } //weight[y]-weight[x]=wとなるように辺を張る bool merge(int x, int y, T w) { w += weight(x); w -= weight(y); x = root(x); y = root(y); if (x == y) { if (weight(y)-weight(x)==w) return true; return false; } if (siz[x] < siz[y]) swap(x, y), w = -w; par[y] = x; diff_weight[y] = w; return true; } }; //連結判定 //グラフGにおいて、startから頂点iが行けるかをvectorで返す vector connected(const vector>& G, int start) { int N = (int)G.size(); stack S; vector seen(N, false); S.push(start); seen[start] = true; while(!S.empty()) { int pos = S.top(); S.pop(); for (int to : G[pos]) { if (!seen[to]) { seen[to] = true; S.push(to); } } } return seen; } //BFS //重みなしグラフにおける最短経路問題 vector bfs(const vector> &G, int start, int impossible) { queue Q; vector dist((int)G.size(), impossible); Q.push(start); dist[start] = 0; while(!Q.empty()) { int pos = Q.front(); Q.pop(); for (int to : G[pos]) { if (dist[to]!=impossible) continue; Q.push(to); dist[to] = dist[pos]+1; } } return dist; } //dijkstra //重み付きグラフにおける最短経路問題 vector dijkstra(const vector>& G, int start, long long impossible) { int N = (int)G.size(); vector dist(N, impossible); priority_queue, vector>, greater>> Q; dist[start] = 0LL; Q.emplace(dist[start], start); while(!Q.empty()) { pair pos = Q.top(); Q.pop(); long long d = pos.first; int v = pos.second; //trash if (dist[v]!=impossible && dist[v]d+e.cost) { dist[e.to] = d+e.cost; Q.push(make_pair(dist[e.to], e.to)); } } } return dist; } //グリッド上でのBFS //スタート位置の文字を与える vector> gridBfs(const vector &s, char start, const string wall, int impossible) { const int vr[4] = {0, 1, 0, -1}, vc[4] = {1, 0, -1, 0}; vector> dist((int)s.size(), vector(s[0].size(), impossible)); queue> Q; //search start for (int i = 0; i < (int)s.size(); i++) { for (int j = 0; j < (int)s[0].size(); j++) { if (s[i][j] == start) { Q.emplace(i, j); dist[i][j] = 0; } } } //BFS while(!Q.empty()) { pair pos = Q.front(); Q.pop(); for (int i = 0; i < 4; i++) { int nr = pos.first+vr[i], nc = pos.second+vc[i]; if (nr < 0 || nr >= (int)s.size() || nc < 0 || nc >= (int)s[0].size()) continue; if (dist[nr][nc] != impossible) continue; if (wall.find(s[nr][nc]) != string::npos) continue; dist[nr][nc] = dist[pos.first][pos.second] + 1; Q.emplace(nr, nc); } } return dist; } //グリッド上でのBFS //スタート位置の行、列を与える vector> gridBfs(const vector &s, int startrow, int startcolumn, const string wall, int impossible) { const int vr[4] = {0, 1, 0, -1}, vc[4] = {1, 0, -1, 0}; vector> dist((int)s.size(), vector(s[0].size(), impossible)); queue> Q; Q.emplace(startrow, startcolumn); dist[startrow][startcolumn] = 0; //BFS while(!Q.empty()) { pair pos = Q.front(); Q.pop(); for (int i = 0; i < 4; i++) { int nr = pos.first+vr[i], nc = pos.second+vc[i]; if (nr < 0 || nr >= (int)s.size() || nc < 0 || nc >= (int)s[0].size()) continue; if (dist[nr][nc] != impossible) continue; if (wall.find(s[nr][nc]) != string::npos) continue; dist[nr][nc] = dist[pos.first][pos.second] + 1; Q.emplace(nr, nc); } } return dist; } //MST //{辺長, u, v} の vector> を渡す long long mst(vector> E, int N, bool m) { int M = (int)E.size(); if (!m) sort(E.begin(), E.end()); else sort(E.begin(), E.end(), greater>()); dsu d(N); long long ret = 0; for (int i=0; i(E[i]), get<2>(E[i]))) { ret+=(long long) get<0>(E[i]); d.merge(get<1>(E[i]), get<2>(E[i])); } } return ret; } //DAG判定 bool isDAG(const vector>& G) { int V = (int)G.size(); vector deg(V, 0); rep(i, 0, V) { for (int to : G[i]) deg[to]++; } queue Q; rep(i, 0, V) if (deg[i] == 0) Q.push(i); vector ans(V, false); while(!Q.empty()) { int v = Q.front(); Q.pop(); ans[v] = true; for (int to : G[v]) { deg[to]--; if (deg[to] == 0) Q.push(to); } } bool k = true; rep(i, 0, V) if (!ans[i]) k = false; return k; } //トポロジカルソート //入次数が0の頂点が最初に来る bool topologicalSort(const vector>& G, vector& res) { assert(res.empty()); int V = (int)G.size(); vector deg(V); rep(i, 0, V) { for (int to : G[i]) deg[to]++; } queue Q; rep(i, 0, V) { if (deg[i] == 0) Q.push(i); } while(!Q.empty()) { int v = Q.front(); Q.pop(); res.push_back(v); for (int to : G[v]) { deg[to]--; if (deg[to] == 0) Q.push(to); } } if ((int)res.size() != V) { res.clear(); return false; } return true; } //素数判定 bool isPrime(long long N) { for (long long d = 2; d * d <= N; d++) { if (N%d == 0) return false; } return true; } //エラトステネスの篩による [1, N] の素数判定 vector primeSieve(int N) { vector isPrime(N+1); isPrime[0] = isPrime[1] = false; for (int i = 2; i <= N; i++) { if (isPrime[i]) { for (int j = 2*i; j <= N; j += i) isPrime[j] = false; } } return isPrime; } //最小素因数 (Least Prime Factor) long long lpf(long long N) { for (long long d = 2; d * d <= N; d++) { if (N%d == 0) { return d; } } return N; } //エラトステネスの篩による [1, N] の最小素因数 vector lpfSieve(int N) { vector lpf(N+1); iota(lpf.begin(), lpf.end(), 0); lpf[0] = lpf[1] = -1; for (int i = 2; i <= N; i++) { if (lpf[i] == i) { for (int j = 2*i; j <= N; j += i) { lpf[j] = min(lpf[j], i); } } } return lpf; } //N の 素因数分解 //{p, e} of N を返す vector> primeFactorization(long long N) { vector> ret; for (long long d = 2; d * d <= N; d++) { if (N%d) continue; int cnt = 0; while(N%d == 0) { cnt++; N /= d; } ret.push_back({d, cnt}); } return ret; } //[1, N] の素因数分解 //[1, N] の lpf 配列を与えて、{{p, e} of 0, {p, e} of 1, ... , {p, e} of N} を返す //0, 1は空 vector>> primeFactorization(vector lpf) { int N = (int)lpf.size()-1; vector>> ret(N+1); for (int i = 2; i <= N; i++) { if (lpf[i] == i) ret[i] = {{i, 1}}; else { ret[i] = ret[i/lpf[i]]; if (ret[i].back().first == lpf[i]) { ret[i].back().second++; } else ret[i].push_back({lpf[i], 1}); } } return ret; } struct combination { vector fac, infac; combination(int n) { fac.resize(n+1); infac.resize(n+1); fac[0] = 1; for (int i = 1; i <= n; i++) fac[i] = fac[i-1] * i; infac[n] = fac[n].inv(); for (int i = n; i >= 1; i--) infac[i-1] = infac[i] * i; } mint operator()(int n, int k) { if (k < 0 || k > n) return 0; return fac[n] * infac[k] * infac[n-k]; } }; struct Hash { vector mod = {998244353, 1000000007, 1000000009, 1000000021, 1000000033}; #ifdef const_rand long long K = const_rand; #else long long K = 100 + rand()%100; #endif vector> Pow; //H[i] = Hash[0, i) vector> H; int B = 5; vector S; int N; void init(string s) { N = (int)s.size(); S.resize(N); for (int i = 0; i(N+1)); for (int i = 0; i m) { mod = m; B = (int)m.size(); init(s); } //val(l, r) = Hash[l, r) vector val(int l, int r) { vector res(B); for (int i = 0; i> runLength(const string& S) { int N = (int)S.size(); vector> ret; for (int l = 0; l>& code) { string ret = ""; for (pair c : code) { ret+=string(c.first, c.second); } return ret; } int digitSum(long long T) { string t = to_string(T); int ret = 0; for (char k : t) ret+=k-'0'; return ret; } template vector compress(const vector& A) { vector B = A; sort(B.begin(), B.end()); B.erase(unique(B.begin(), B.end()), B.end()); vector res((int)A.size()); for (int i = 0; i < (int)A.size(); i++) { res[i] = lower_bound(B.begin(), B.end(), A[i])-B.begin(); } return res; } //vectorであらわされるn進数を10進数に変換する template T changeBaseToTen(const vector& A, T n) { T ret = 0; T mul = 1; for (int i = (int)A.size()-1; i >= 0; i--) { ret += mul * A[i]; mul *= n; } return ret; } //10進数でxのものをn進数に変換する template vector changeBaseFromTen(T x, T n) { vector ret; while(x != 0) { ret.push_back(x%n); x /= n; } reverse(ret.begin(), ret.end()); return ret; } //n進法の桁のvectorであるSをm進法に変換する template vector changeBase(const vector& S, T n, T m) { T ten = changeBaseToTen(S, n); return changeBaseTromTen(ten, m); } int H, W; int gi, gj; vector di = {1, -1, 0, 0}, dj = {0, 0, 1, -1}; int ans = 0; vector> S; void dfs(int ci, int cj) { if (ci == gi and cj == gj) { ans++; return; } S[ci][cj] = true; rep(k, 0, 4) { int ni = ci + di[k], nj = cj + dj[k]; if (ni < 0 || nj < 0 || ni >= H || nj >= W) continue; int cnt = 0; rep(nk, 0, 4) { int nni = ni + di[nk], nnj = nj + dj[nk]; if (nni < 0 || nnj < 0 || nni >= H || nnj >= W) continue; cnt += S[nni][nnj]; } if (cnt >= 2) continue; if (S[ni][nj]) continue; dfs(ni, nj); } S[ci][cj] = false; } int main() { cin >> H >> W; int si, sj; cin >> si >> sj; si--; sj--; cin >> gi >> gj; gi--; gj--; S.resize(H, vector(W, false)); dfs(si, sj); cout << ans << endl; }