#include <bits/stdc++.h>
#define int long long int
using namespace std;
template<typename T,typename U> using P=pair<T,U>;
template<typename T> using V=vector<T>;
template<typename T>bool chmax(T&a,T b){if(a<b){a=b;return true;}return false;}
template<typename T>bool chmin(T&a,T b){if(a>b){a=b;return true;}return false;}

template<typename T>auto&operator<<(ostream&s,const vector<T>&v){s<<"[";bool a=1;for(auto e:v){s<<(a?"":" ")<<e;a=0;}s<<"]";return s;}
template<typename T,typename U>auto&operator<<(ostream&s,const pair<T,U>&p){s<<"("<<p.first<<","<<p.second<<")";return s;}
template<typename T>auto&operator<<(ostream&s,const set<T>&st){s<<"{";bool a=1;for(auto e:st){s<<(a?"":" ")<<e;a=0;}s<<"}";return s;}
template<typename T,typename U>auto&operator<<(ostream&s,const map<T,U>&m){s<<"{";bool a=1;for(auto e:m){s<<(a?"":" ")<<e.first<<":"<<e.second;a=0;}s<<"}";return s;}
#define DUMP(x) cout<<#x<<" = "<<(x)<<endl
#define COUT(x) cerr<<#x<<" = "; cout<<(x)<<endl

struct edge { int to, cost; };

const int INF = 1e18;
const int MOD = 1e9+7;

template<typename Field>
struct Perm {
   vector<vector<Field>> p;
   Perm(int n) {
      p.resize(n+1, vector<Field>(n+1));
      for (int i = 0; i <= n; i++) {
         for (int j = 0; j <= i; j++) {
            if (i == 0 || j == 0) {
               p[i][j] = 1;
            } else {
               p[i][j] = p[i-1][j-1] * j + p[i-1][j];
            }
         }
      }
   }
   Field operator()(int n, int r) {
      if (n < 0 || n-r < 0 || r < 0) return 0;
      return p[n][r];
   }
};

template<int mod=MOD>
struct ModRing {
   int x;
   ModRing() = default;
   ModRing(int a) { x = ((a % mod) + mod) % mod; }

   ModRing& operator=(const ModRing& a) & = default;

   const ModRing operator+() const { return ModRing(x); }
   const ModRing operator-() const { return ModRing((-x + mod) % mod); }

   ModRing& operator+=(const ModRing& a) { (x += a.x) %= mod; return *this; }
   ModRing& operator-=(const ModRing& a) { return *this += -a; }
   ModRing& operator*=(const ModRing& a) { (x *= a.x) %= mod; return *this; }

   ModRing& operator++() { return *this += 1; }
   ModRing& operator--() { return *this -= 1; }

   const ModRing operator+(const ModRing& a) const { return ModRing(*this) += a; }
   const ModRing operator-(const ModRing& a) const { return ModRing(*this) -= a; }
   const ModRing operator*(const ModRing& a) const { return ModRing(*this) *= a; }

   bool operator<(const ModRing& a) const { return x < a.x; }
   bool operator>(const ModRing& a) const { return a < *this; }
   bool operator<=(const ModRing& a) const { return !(*this > a); }
   bool operator>=(const ModRing& a) const { return !(*this < a); }
   bool operator==(const ModRing& a) const { return !(*this < a) && !(*this > a); }
   bool operator!=(const ModRing& a) const { return !(*this == a); }
};

template<int mod>auto&operator<<(ostream&s,const ModRing<mod>&a){s<<a.x;return s;}

template<int mod=MOD>
struct FiniteField : ModRing<mod> {
   FiniteField() = default;
   FiniteField(int a) : ModRing<mod>(a) {}
   FiniteField(const ModRing<mod>& a) : ModRing<mod>(a) {}

   FiniteField& operator=(const FiniteField& a) & = default;

   const FiniteField operator+() const { return ModRing<mod>::operator+(); }
   const FiniteField operator-() const { return ModRing<mod>::operator-(); }

   FiniteField& operator+=(const FiniteField& a) { return *this = ModRing<mod>::operator+=(a); }
   FiniteField& operator-=(const FiniteField& a) { return *this = ModRing<mod>::operator-=(a); }
   FiniteField& operator*=(const FiniteField& a) { return *this = ModRing<mod>::operator*=(a); }

   FiniteField& operator++() { return *this = ModRing<mod>::operator++(); }
   FiniteField& operator--() { return *this = ModRing<mod>::operator--(); }

   const FiniteField operator+(const FiniteField& a) const { return ModRing<mod>::operator+(a); }
   const FiniteField operator-(const FiniteField& a) const { return ModRing<mod>::operator-(a); }
   const FiniteField operator*(const FiniteField& a) const { return ModRing<mod>::operator*(a); }

   const FiniteField power(int n) const {
      FiniteField tmp = *this, res = 1;
      while (n > 0) { if ((n & 1) == 1) res *= tmp; tmp *= tmp; n >>= 1; }
      return res;
   }
   const FiniteField inverse() const {
      try { if (this->x == 0) throw "0 have no inverse"; }
      catch (const char* e) { cerr << e << endl; }
      return power(mod-2);
   }
   FiniteField& operator/=(const FiniteField& a) { return *this = (*this *= a.inverse()); }
   const FiniteField operator/(const FiniteField& a) const { return FiniteField(*this) /= a; }
};
using GF = FiniteField<>;

signed main()
{
   int n, m; cin >> n >> m;
   V<int> a(n), b(m);
   for (int i = 0; i < n; i++) {
      cin >> a[i];
      a[i]--;
   }
   for (int i = 0; i < m; i++) {
      cin >> b[i];
      b[i]--;
   }

   set<int> ast(a.begin(), a.end()), bst(b.begin(), b.end());
   if (ast.size() < n || bst.size() < m) {
      cout << "0" << endl;
      return 0;
   }

   V<int> A(n*m), B(n*m);
   for (auto i : ast) A[i] = 1;
   for (auto i : bst) B[i] = 1;

   V<int> Asum(n*m+1), Bsum(n*m+1);
   for (int i = 0; i < n*m; i++) Asum[i+1] = Asum[i] + A[i];
   for (int i = 0; i < n*m; i++) Bsum[i+1] = Bsum[i] + B[i];

   GF ans = 1;
   for (int i = n*m-1; i >= 0; i--) {
      if (A[i] && B[i]) {
         ans *= 1;
      } else if (A[i] && !B[i]) {
         ans *= m - Bsum[i];
      } else if (!A[i] && B[i]) {
         ans *= n - Asum[i];
      } else {
         ans *= (n - Asum[i]) * (m - Bsum[i]) - (n*m - 1 - i);
      }
   }
   cout << ans << endl;

   return 0;
}

Measured memory_bytes: 31616