因为反正也不知道哪个集合对应哪个,可以先将点集做一个映射,所以我们可以将点集变成{1,2,3,4}{5,6}{7,8,9}类似这样,好看又方便处理
每个点集连向的点要么是他前面要么后面,可以f[i][j]表示前i个点集,还有j个点集没有匹配点,图的方案数,因为知道了i和j,前面还有多少个点可供匹配就知道了,没有必要用额外的一维表示
然后就可以dp啦
转移的话我是每个点集内枚举每个点转移,f[i][j][0/1]在之前的基础上表示当前点集是否连向前面0/1,点集内转移完再f[i][j][1]转移到f[i][j][0]
emmmmmmm我知道挺麻烦的但我好像找不到更简便的方法….
code:
#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;
const int maxn = 1100;
const int mod = 1e9+7;
inline void add(int &a,const int &b){a+=b;if(a>=mod)a-=mod;}
int n;
bool v[maxn];
int p[maxn],pn;
int f[2][maxn][2],now;
int re;
void dp()
{
now=0; memset(f,0,sizeof f);
f[now][0][0]=1; int sum=0;
for(int i=1;i<=pn;i++)
{
now=!now;
for(int k=0;k<=i-1;k++) if(f[!now][k][0])
{
int &temp=f[!now][k][0];
add(f[now][k][0],temp*(ll)(sum-(i-1-k))%mod);
add(f[now][k][1],temp);
temp=0;
}
for(int j=1;j<=p[i];j++)
{
now=!now;
for(int k=0;k<i;k++) for(int l=0;l<2;l++) if(f[!now][k][l])
{
int &temp=f[!now][k][l];
if(k) add(f[now][k-1][l],temp*(ll)k%mod);
add(f[now][k][l],temp);
temp=0;
}
}
now=!now;
for(int k=0;k<i;k++) for(int l=0;l<2;l++) if(f[!now][k][l])
{
int &temp=f[!now][k][l];
add(f[now][k+l][0],temp);
temp=0;
}
sum+=p[i];
}
for(int j=1;j<=n-sum;j++)
{
now=!now;
for(int k=0;k<=pn;k++) if(f[!now][k][0])
{
int &temp=f[!now][k][0];
add(f[now][k][0],temp);
if(k) add(f[now][k-1][0],temp*(ll)k);
temp=0;
}
}
re=f[now][0][0];
}
int main()
{
int tcase=0;
while(scanf("%d",&n)!=EOF&&n)
{
for(int i=1;i<=n;i++) v[i]=false;
pn=0;
for(int i=1;i<=n;i++)
{
int cc; scanf("%d",&cc);
if(cc>0)
{
p[++pn]=cc;
while(cc--)
{
int x; scanf("%d",&x);
if(v[x]) { n=0;break; }
v[x]=true;
}
}
}
re=0;
if(n) dp();
printf("Case #%d: %d\n",++tcase,re);
}
return 0;
}