题意:
设
d
i
s
x
dis_x
disx为x到1的唯一最短路(必须唯一),则满足每个点度数=
d
x
d_x
dx且
d
i
s
x
<
=
d
i
s
x
+
1
dis_x<=dis_{x+1}
disx<=disx+1的图有多少种
n
<
=
50
,
2
<
=
d
x
<
=
3
n<=50,2<=d_x<=3
n<=50,2<=dx<=3
题解:
显然按
d
i
s
dis
dis这个图将这个图划分成一段段,每个点会向前一段连一条边,剩下的边就是段内互连
一个比较naive的想法就是暴力枚举段的划分方法,然后状压上一段每个点的剩余度数dp,当需要新开一段的时候,也就是需要段内互连的时候,转移需要预处理一个
g
[
i
]
[
j
]
g[i][j]
g[i][j]表示i个度数为1的点,j个读度数为2的点(有标号但无顺序)互连的方案数,这个很好dp具体代码
容易发现具体哪个点剩余度数是无所谓的,我们只关心度数为1的点和度数为2的点有多少
于是可以直接dp,
f
[
i
]
[
j
]
[
a
]
[
b
]
f[i][j][a][b]
f[i][j][a][b]表示到i个前一段的右端点是j,前一段的1,2度数个数分别为a,b直接转移即可
code:
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#define LL long long
using namespace std;
const LL mod=1e9+7;
LL g[55][55],d[55],n;
LL c[3][55],f[55][55][55][55];
LL C(LL n) {return n*(n-1)/2%mod;}
void add(LL &x,LL y) {x+=y;x-=x>=mod?mod:0;}
void pre()
{
g[0][0]=1;
for(LL i=2;i<=n;i+=2) g[i][0]=(i-1)*g[i-2][0]%mod;
for(LL j=1;j<=n;j++)
for(LL i=0;i+j<n;i++)
{
if(j>=3) (g[i][j]+=C(j-1)*g[i+2][j-3]%mod)%=mod;
if(j>=2) (g[i][j]+=(j-1)*i%mod*g[i][j-2]%mod)%=mod;
if(i>=2) (g[i][j]+=C(i)*g[i-2][j-1]%mod)%=mod;
}
}
LL get(LL l,LL r,LL op) {return c[op][r]-c[op][l];}
int main()
{
scanf("%lld",&n);
for(LL i=1;i<=n;i++) scanf("%lld",&d[i]);
pre();
for(LL i=2;i<=n;i++)
{
c[1][i]=c[1][i-1];c[2][i]=c[2][i-1];
c[d[i]-1][i]++;
}
f[d[1]+1][d[1]+1][c[1][d[1]+1]][c[2][d[1]+1]]=1;
for(LL i=d[1]+1;i<=n;i++)
for(LL j=1;j<=i;j++)
for(LL a=0;a<=i;a++)
for(LL b=0;a+b<=i;b++) if(f[i][j][a][b])
{
if(i!=j) add(f[i][i][get(j,i,1)][get(j,i,2)],f[i][j][a][b]*g[a][b]%mod);
if(i!=n&&a) add(f[i+1][j][a-1][b],f[i][j][a][b]*a%mod);
if(i!=n&&b) add(f[i+1][j][a+1][b-1],f[i][j][a][b]*b%mod);
}
LL ans=0;
for(LL a=0;a<=n;a++)
for(LL b=0;a+b<=n;b++)
{
if(!f[n][n][a][b]) continue;
add(ans,f[n][n][a][b]*g[a][b]%mod);
}
printf("%lld",ans);
}