非常神的 DP 思维题。
不难想到我们枚举有哪些牛没有匹配,如果牛 \(i\) 没有匹配,那么所有 \(\ge s_i\) 的 牛棚 \(t_j\) 必须匹配。
所以我们只关心最小的没有被匹配的牛。
接下来是这题最关键的一步,我们把牛和牛棚放到一起排序。
因为我们只关心相对大小,放到一起后可以极大程度简化状态。
所以我们可以设计状态 \(f_{i,j,0/1}\),表示枚举到第 \(i\) 个元素,有 \(j\) 头等待匹配的牛,前面是否有牛放弃匹配。
对于第 \(i\) 个元素是牛还是牛棚分开讨论,时间复杂度 \(\mathcal{O}(N^2)\)。
#define N 3005
int n, f[N + N][N][2];
struct node{
int w, op;
bool operator<(const node o)const{
if(w != o.w)return w < o.w;
return op < o.op;
}
}a[N << 1];
int main() {
read(n);
rp(i, n)read(a[i].w), a[i].op = 0;
rp(i, n)read(a[i + n].w), a[i + n].op = 1;
sort(a + 1, a + n + n + 1);
f[0][0][0] = 1;
rp(i, n + n)rep(j, 0, n){
if(!a[i].op){
ad(f[i][j + 1][0], f[i - 1][j][0]),
ad(f[i][j + 1][1], f[i - 1][j][1]),
ad(f[i][j][1], f[i - 1][j][1]);
ad(f[i][j][1], f[i - 1][j][0]);
}
else{
ad(f[i][j][0], f[i - 1][j + 1][0] * (j + 1LL) % P);
ad(f[i][j][0], f[i - 1][j][0]);
ad(f[i][j][1], f[i - 1][j + 1][1] * (j + 1LL) % P);
}
}
cout << (f[n + n][0][0] + f[n + n][0][1]) % P << endl;
return 0;
}