Description
给定一个长为 \(2n\) 的序列 \(A\),其中 \(A\) 的每一位要么是 \(-1\),要么是 \(1\sim 2n\) 的一个数。将每个 \(A_i=-1\) 替换为 \(1 \sim 2n\) 的某个数,使 \(A\) 成为一个长 \(2n\) 的排列。令长为 \(n\) 的序列 \(B\): \(B_i=\min \{A_{2i-1},A_{2i}\}\),求可能的 \(B\) 序列个数。
Solution
可以将原序列分为 \(n\) 个组,组内下标为 \(2i-1,2i\)。
那么有 3 种情况:
- 两个数分别为 \(-1,-1\)。
- 其中一个为 \(-1\),另一个的值是确定的。
- 两个数的值都是确定的。
第 3 种情况是朴素的,因为该组生成的 \(B_i\) 是确定的,可以考虑直接排除掉。
考虑剩下两种情况。由于 \(\min\) 的存在,很容易想到从大到小枚举填哪个值。我们发现原序列的各组顺序没有影响,因此钦定 \(B\) 从大到小排序。考虑 dp,每次转移时我们需要知道:
满足情况 1 的组个数,满足情况 2 的组个数。
如果继续这样思考仍然是可做的,但是不妨换一个角度。我们只关心 \(-1\) 的个数和 \(A_i\neq -1\) 的数的个数。首先每个 \(A_i\neq-1\) 对应一个情况 2 的组。那么相当于一个匹配问题,每个 \(A_i=-1\) 可以对应一个 \(A_j=-1\) 或 \(A_{j}\neq -1\),这样匹配完成了过后,等价于原序列中的情况 1. 和 2. 的组。
那么原问题转化为(先删掉所有情况 3 的组):
有若干个 \(-1\) 和值域在 \(1 \sim 2n\) 的数。每个 \(\neq -1\) 的数都必须匹配一个 \(-1\),\(-1\) 可以任意匹配。
匹配完成后,将 \(-1\) 替换为 \(1 \sim 2n\) 的值,要求所有数互不相同,求每对匹配的最小值序列个数。
定义 \(f_{i,j,k}\) 表示考虑填一个值 \(i\),前面有 \(j\) 个 \(-1\),\(k\) 个 \(\neq -1\) 的方案数:
- 原序列存在 \(i\):
- 选择匹配一个 \(-1\),\(f_{i,j,k} \to f_{i-1,j-1,k}\)。
- 选择不匹配,与后面的匹配:\(f_{i,j,k} \to f_{i-1,j,k+1}\)。
- 原序列不存在 \(i\):(当前这个值一定是从 \(-1\) 变过来的)
- 选择匹配一个 \(-1\):\(f_{i,j,k} \to f_{i-1,j-1,k}\)。
- 选择匹配一个 \(\neq -1\)(\(\neq-1\) 的原序列对应组是确定的,因此互不相同,乘一个系数):\(f_{i,j,k}\times k \to f_{i,j,k-1}\)
- 不匹配,与后面的匹:\(f_{i,j,k} \to f_{i,j+1,k}\)。
初值:\(f_{2n,0,0}\),答案:\(f_{0,0,0} \times c!\)。\(c\) 是原序列 \((-1,-1)\) 组的个数,由于刚刚没考虑他们顺序,需要乘一个系数。
code
Click to View Code
#include <bits/stdc++.h>
#define rep(i, l, r) for (int i = l; i <= r; ++i)
#define per(i, r, l) for (int i = r; i >= l; --i)
#define int long long
#define LL long long
#define ull unsigned long long
#define FRE(p) freopen(#p".in", "r", stdin),freopen(#p".out","w",stdout)
#define DBG(p) freopen(#p".in", "r", stdin),freopen("my.out","w",stdout)
#define FASTIO ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
using namespace std;
const int N = 605, M = 505, mod = 1e9+7;
int n, a[N], f[N][N][N];
int bj[N];
inline void cadd(int& x, int y) {x = (x+y)%mod;
}
int fac[N];
signed main() {
#ifdef LOCALFRE(bomb);
#endifFASTIO;cin >> n;fac[0] = 1;rep(i, 1, (n<<1)) cin >> a[i], bj[max(0ll, a[i])]=1, fac[i] = fac[i-1]*i%mod;int tmp = 0;rep(i, 1, n) {tmp += a[i<<1]==-1&&a[(i<<1)-1]==-1;if (a[i<<1]>0&&a[(i<<1)-1]>0) {bj[a[i<<1]] = 2, bj[a[(i<<1)-1]] = 2;}}f[n<<1][0][0] = 1;int cnt = 0;per(i, (n<<1), 1) {rep(j, 0, cnt) {rep(k, 0, (n<<1)-i-cnt) {if (bj[i]==2) cadd(f[i-1][j][k], f[i][j][k]);else if (bj[i]==1) {cadd(f[i-1][j][k+1], f[i][j][k]);if (j > 0) cadd(f[i-1][j-1][k], f[i][j][k]);} else {cadd(f[i-1][j+1][k], f[i][j][k]);if (j > 0) cadd(f[i-1][j-1][k], f[i][j][k]);if (k > 0) cadd(f[i-1][j][k-1], k*f[i][j][k]);}}}cnt += bj[i]==0;}cout << f[0][0][0]*fac[tmp]%mod << '\n';return 0;
}
/*
morning: *******************
afternoon: *********
*/