unittest
{
	assert( [ "2 2", "4 3", "3 4" ].parse.expand.solve == 2 );
	assert( [ "3 3", "5 9 7", "3 6 9" ].parse.expand.solve == 0 );
	assert( [ "2 2", "4 4", "4 4" ].parse.expand.solve == 0 );
	assert( [ "14 13", "158 167 181 147 178 151 179 182 176 169 180 129 175 168", "181 150 178 179 167 180 176 169 182 177 175 159 173" ].parse.expand.solve == 343772227 );
}



import std.algorithm;
import std.conv;
import std.range;
import std.stdio;
import std.typecons;

void main()
{
	stdin.byLineCopy.parse.expand.solve.writeln;
}

auto parse( Range )( Range input )
if( isInputRange!Range && is( ElementType!Range == string ) )
{
	auto nm = input.front.split.to!( long[] );
	input.popFront;
	auto as = input.front.split.to!( long[] );
	input.popFront;
	auto bs = input.front.split.to!( long[] );
	return tuple( as, bs );
}

auto solve( long[] as, long[] bs )
{
	const M = 10L ^^ 9 + 7;
	
	as.sort!( ( x, y ) => y < x );
	bs.sort!( ( x, y ) => y < x );
	
	// 複数行、複数列に同じ数を書くことはできない
	foreach( i; 1L .. as.length.signed )
	{
		if( as[ i - 1 ] == as[ i ] ) return 0L;
	}
	foreach( i; 1L .. bs.length.signed )
	{
		if( bs[ i - 1 ] == bs[ i ] ) return 0L;
	}
	
	// 大きい数から書き込む
	auto ans = ModWorld!M( 1L );
	auto c = 0L;
	auto h = 0L;
	auto w = 0L;
	foreach_reverse( i; 1L .. as.length.signed * bs.length.signed + 1 )
	{
		// 書き込める範囲を判定
		while( h < as.length && i <= as[ h ] ) h++;
		while( w < bs.length && i <= bs[ w ] ) w++;
		
		// 書き込めるマスの数を計算
		auto t = 0L;
		if( 0 < h && as[ h - 1 ] == i && 0 < w && bs[ w - 1 ] == i )
		{
			// 一か所に決まる場合
			t = 1;
		}
		else if( 0 < h && as[ h - 1 ] == i )
		{
			// 1行に決まる場合
			t = w;
		}
		else if( 0 < w && bs[ w - 1 ] == i )
		{
			// 1列に決まる場合
			t = h;
		}
		else
		{
			// 範囲内ならどこでもいい場合
			t = h * w - c;
			if( t <= 0 ) return 0L;
		}
		c++;
		
		// パターン数を計算
		ans *= t;
	}
	return ans.n;
}

// モジュラ整数型（n mod M）
struct ModWorld( long M )
{
	long n;
	
	void opUnary( string OP )() if( OP == "++" ){ this.n = ( this.n + 1 ) % M; }
	void opUnary( string OP )() if( OP == "--" ){ this.n = ( this.n + M - 1 ) % M; }
	void opOpAssign( string OP )( ModWorld r ) if( OP == "+"  ){ this.n = ( this.n + r.n ) % M; }
	void opOpAssign( string OP )( ModWorld r ) if( OP == "-"  ){ this.n = ( this.n + M - r.n ) % M; }
	void opOpAssign( string OP )( ModWorld r ) if( OP == "*"  ){ this.n = ( this.n * r.n ) % M; }
	void opOpAssign( string OP )( ModWorld r ) if( OP == "/"  ){ this *= r ^^ ModWorld( M - 2 ); }
	void opOpAssign( string OP )( ModWorld r ) if( OP == "^^" )
	{
		auto a = this.n;
		this.n = 1L;
		for( auto i = r.n; 0 < i; i >>= 1 )
		{
			if( i & 1 ) this.n = ( this.n * a ) % M;
			a = ( a * a ) % M;
		}
	}
	auto opBinary( string OP )( ModWorld r ){ auto l = this; l.opOpAssign!OP( r ); return l; }
	
	void opAssign( long r ){ this.n = r; }
	void opOpAssign( string OP )( long r ){ this.opOpAssign!OP( ModWorld( r ) ); }
	auto opBinary( string OP )( long r ){ return this.opBinary!OP( ModWorld( r ) ); }
	auto opBinaryRight( string OP )( long l ){ return ModWorld( l ).opBinary!OP( this ); }
	auto opEquals( long r ){ return this.n == r; }
	
	auto toString(){ return this.n.to!string; }
}
