解法
考虑如果A中的两个相邻位置都确定了的话,这两个位置所对应的B的位置不会对B的方案产生影响,直接不管就可以了。
如果建立图论模型,可以发现B的每一个数相当于对A中的两个数匹配后,较小的值
然后记录每个数是否在A中出现过,然后可以从大到小dp:
f
[
i
]
[
j
]
[
k
]
f[i][j][k]
f[i][j][k]表示考虑了
≥
i
\ge i
≥i的所有位置,其中有
j
j
j个已经在A中出现的还需要向比i更小的位置匹配,有
k
k
k个没有在A中出现的需要向比i更小的位置匹配。
转移考虑从
i
+
1
i+1
i+1转移过来:
讨
论
i
是
否
在
A
中
出
现
过
:
讨论i是否在A中出现过:
讨论i是否在A中出现过:
如
果
出
现
过
:
f
[
i
]
[
j
]
[
k
]
+
=
f
[
i
+
1
]
[
j
−
1
]
[
k
]
如果出现过:f[i][j][k]+=f[i+1][j-1][k]
如果出现过:f[i][j][k]+=f[i+1][j−1][k],这里的意思是i也要和更小的数匹配
如
果
出
现
过
:
f
[
i
]
[
j
]
[
k
]
+
=
f
[
i
+
1
]
[
j
]
[
k
+
1
]
如果出现过:f[i][j][k]+=f[i+1][j][k+1]
如果出现过:f[i][j][k]+=f[i+1][j][k+1],这里是i和没有出现在A中的更大的数匹配。注意i不能和已经出现在A中的更大的数匹配,因为如果要匹配的话,这两个数必须相邻,这种情况一开始已经考虑过了$
如果没出现过,则可以和更小的数匹配,或者和任意更大的数匹配,但是如果匹配的是已经出现过的更大的数,因为匹配任意一个都可以,所以要乘上个数,如果是未出现的,因为所有未出现的可以互相替换顺序,所以一开始算上一个所有未出现位置个数的阶乘
#include<bits/stdc++.h>
using namespace std;
const int maxn=605;
const int mod=1e9+7;
inline int read(){
char c=getchar();int t=0,f=1;
while((!isdigit(c))&&(c!=EOF)){if(c=='-')f=-1;c=getchar();}
while((isdigit(c))&&(c!=EOF)){t=(t<<3)+(t<<1)+(c^48);c=getchar();}
return t*f;
}
int n,a[maxn],b[maxn],t,c[maxn];
int f[2][maxn][maxn];
inline void add(int &a,int b){
a=a+b;
if(a>=mod)a=a-mod;
if(a<0)a=a+mod;
}
signed main(){
n=read();
for(int i=1;i<=2*n;i++)a[i]=read();
int cnt=0;
for(int i=1;i<=2*n;i+=2){
if((a[i]!=-1)&&(a[i+1]!=-1))b[a[i]]=b[a[i+1]]=2;
else if(a[i]!=-1){b[a[i]]=1;}
else if(a[i+1]!=-1){b[a[i+1]]=1;}
else cnt++;
}
for(int i=1;i<=2*n;i++){
if(b[i]==1)c[++t]=1;
else if(b[i]==0)c[++t]=2;
}
f[t&1][0][0]=1;
for(int i=t;i>=1;i--){
memset(f[(i-1)&1],0,sizeof(f[(i-1)&1]));
for(int j=0;j<=t&&j<=n;j++){
for(int k=0;k<=t&&k<=n;k++){
if(c[i]==1){
add(f[(i-1)&1][j+1][k],f[i&1][j][k]);
if(k)add(f[(i-1)&1][j][k-1],f[i&1][j][k]);
}
else{
add(f[(i-1)&1][j][k+1],f[i&1][j][k]);
if(k)add(f[(i-1)&1][j][k-1],f[i&1][j][k]);
if(j)add(f[(i-1)&1][j-1][k],(1ll*f[i&1][j][k]*j)%mod);
}
}
}
}
int ans=f[0][0][0];
for(int i=1;i<=cnt;i++)
ans=1ll*ans*i%mod;
printf("%d\n",ans);
return 0;
}