Address
Solution
將每個 和 分爲一組,簡單討論一下不同情況下的限制。
若 都不爲 , 是確定的,可以直接將這兩個元素刪去。
於是問題可以轉化爲:
給定一個圖,圖中有 個點,其中一些點有標記(原排列中不爲 的所有元素),求圖中有標記的點之間沒有邊相連的本質不同的完美匹配的方案數。
定義兩組完美匹配本質相同當且僅當以下兩個條件都成立:
- 對於每個有標記的點,兩組匹配中的匹配點要麼相同,要麼編號都比該點大;
- 將所有不包含標記點的匹配按照頂點編號的最小值排序,得到的頂點編號的最小值的序列相同。
轉化的問題中實際上是忽略了不包含標記點的匹配之間的順序,實際情況中因爲不包含標記點的匹配數是固定的,可以最後再計算。
考慮按編號從大到小安排匹配,設 表示已經處理了編號 的點,其中未匹配的有標記的點有 個、未匹配的沒有標記的點有 個的方案數。
容易得到轉移:
-
若 有標記,
-
若 沒有標記,
設不包含標記點的匹配數爲 ,最後的答案爲 。
時間複雜度 。
Code
#include <bits/stdc++.h>
template <class T>
inline void read(T &res)
{
char ch; bool flag = false; res = 0;
while (ch = getchar(), !isdigit(ch) && ch != '-');
ch == '-' ? flag = true : res = ch ^ 48;
while (ch = getchar(), isdigit(ch))
res = res * 10 + ch - 48;
flag ? res = -res : 0;
}
const int N = 305;
const int M = 605;
const int mod = 1e9 + 7;
int s1[M], s2[M], a[M], b[M], f[2][N][M];
bool tag[M], del[M];
int n, m, cnt;
inline void add(int &x, int y)
{
x += y;
x >= mod ? x -= mod : 0;
}
inline void Delete(int x)
{
del[x] = true;
for (int i = 1; i <= m; ++i)
if (!del[i] && a[i] > a[x])
--a[i];
}
int main()
{
read(n);
m = n << 1;
for (int i = 1; i <= m; ++i)
read(a[i]);
for (int i = 1; i <= m; i += 2)
if (~a[i] && ~a[i + 1])
{
Delete(i);
Delete(i + 1);
}
n = 0;
for (int i = 1; i <= m; i += 2)
if (!del[i])
{
if (~a[i])
tag[a[i]] = true;
if (~a[i + 1])
tag[a[i + 1]] = true;
if (a[i] == -1 && a[i + 1] == -1)
++cnt;
++n;
}
m = n << 1;
f[m + 1 & 1][0][0] = 1;
for (int i = m; i >= 1; --i)
{
int now = i & 1, lst = now ^ 1;
s1[i] = s1[i + 1] + tag[i];
s2[i] = s2[i + 1] + !tag[i];
if (tag[i])
{
for (int j = 0; j <= s1[i]; ++j)
for (int k = 0; k <= s2[i]; ++k)
{
f[now][j][k] = j > 0 ? f[lst][j - 1][k] : 0;
add(f[now][j][k], f[lst][j][k + 1]);
}
}
else
{
for (int j = 0; j <= s1[i]; ++j)
for (int k = 0; k <= s2[i]; ++k)
f[now][j][k] = (1ll * f[lst][j + 1][k] * (j + 1)
+ (k > 0 ? f[lst][j][k - 1] : 0) + f[lst][j][k + 1]) % mod;
}
}
int res = f[1][0][0];
for (int i = 1; i <= cnt; ++i)
res = 1ll * res * i % mod;
printf("%d\n", res);
return 0;
}