题意:给出 个点,和每个点的度让你构造出一张无向图满足以下两条性质:
1.点 到点 仅有唯一一条最短路
2.点 到点 的最短路长度大于等于点 到点 的最短路长度
求能构成满足条件的无向图的个数?
首先我们可以根据上面的两点推出一些结论:
1.根据第一条性质显然这张图是由一棵树加上一些平边构成的(连接深度相同两点的边)
2.根据第二条性质显然 所在的点一定在点 的同层或下一层
由此可知,对于两层之间的方案数转移,我们只需要考虑这一层有几个点,上一层有几个度为 的点,有几个度为 的点。
假设 表示当前层有 个点,上一层有 个度为 的点, 个度为 的点的方案数。
开始考虑这个转移:
,相当于只有不取一种方案
每个点度都为 所以一定是若干个项链,为了避免重复计算,我们假设项链里一定包含最后一个点,所以方案数为——将最后一个点拿出来与之前任选两个、三个……k-1个点分别构成项链的种类数之和,其中项链数表示选 个点构成排列,首尾成环且翻转算重复的方案数, ,这里l+1取大于二的原因是因为题目中说两个点之间不能有两条边,所以两个点组成的项链不符合题意。
同样是连接最后一个点使其度数为
第一种情况表示在 个可能变成 的点里取一个和最后的点连成两个度数为2的点
第二种情况表示在 个可能变成 的点里取一个和最后的点连接,之前二度的点变成三度,最后一个点变成二度,所以转移时 不变, 从 转移
第一种情况表示上一行选出一个可能变成 的点,和当前行最后一个点相连,显然上一行只有一个数变成了二度,所以从 转移
第二种情况为上一行选出一个可能变成 的点,和当前行最后一个点相连,显然上一行原来有一个二度的点变成了三度,所以 从 转移, 从 转移
好的,我们思考完了 的转移,开始引入下一个数组
定义 表示当前点是第 个,之前的 、 ……等 个点与 在同一层的方案数
的转移方程还是好求的:
其中
、
分别表示这
个数中有
个度为
的点,
个度为
的点,这个方程就不讲解了,可以根据之前的定义自己感受一下。
那么答案就出来了
其中
、
分别表示这
个数中有
个度为
的点,
个度为
的点
代码如下:
#include<set>
#include<map>
#include<cmath>
#include<queue>
#include<stack>
#include<vector>
#include<cstdio>
#include<string>
#include<cstring>
#include<iostream>
#include<algorithm>
#define mod 1000000007
using namespace std;
int n,d[60];
long long a[60],g[60][60][60],f[60][60],c[60][60];
int main()
{
a[0]=a[1]=0;
a[2]=a[3]=1;
for(int i=4;i<=50;i++)
{
a[i]=a[i-1]*(i-1)%mod;
}
for(int i=0;i<=50;i++)
{
c[i][i]=c[i][0]=1;
}
for(int i=1;i<=50;i++)
{
for(int j=1;j<i;j++)
{
c[i][j]=c[i-1][j-1]+c[i-1][j];
c[i][j]%=mod;
}
}
g[0][0][0]=1;
for(int j=0;j<=50;j++)
{
for(int k=0;k<=50-j;k++)
{
if(j==0&&k>0)
{
for(int l=2;l<k;l++)
{
g[0][j][k]+=(g[0][j][k-l-1]*c[k-1][l])%mod*a[l+1]%mod;
g[0][j][k]%=mod;
}
}
else
{
if(j>=2) g[0][j][k]+=(g[0][j-2][k]*(j-1))%mod,g[0][j][k]%=mod;
if(k>=1) g[0][j][k]+=(g[0][j][k-1]*k)%mod,g[0][j][k]%=mod;
}
}
}
for(int i=1;i<=50;i++)
{
for(int j=0;j<=50-i;j++)
{
for(int k=0;k<=50-i-j;k++)
{
if(j>=1) g[i][j][k]+=(g[i-1][j-1][k]*j)%mod,g[i][j][k]%=mod;
if(k>=1) g[i][j][k]+=(g[i-1][j+1][k-1]*k)%mod,g[i][j][k]%=mod;
}
}
}
int c1,c2;
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&d[i]);
f[d[1]+1][d[1]]=1;
for(int i=d[1]+2;i<=n;i++)
{
for(int j=1;j<=i-d[1]-1;j++)
{
c1=c2=0;
for(int k=1;k<=i-j;k++)
{
if(d[i-j-k+1]==2) c1++;
else c2++;
f[i][j]+=f[i-j][k]*g[j][c1][c2]%mod;
f[i][j]%=mod;
}
}
}
c1=c2=0;
long long ans=0;
for(int j=1;j<n;j++)
{
if(d[n-j+1]==2)c1++;
else c2++;
ans+=f[n][j]*g[0][c1][c2]%mod;
ans%=mod;
}
printf("%lld\n",ans);
}