给定长 的序列 ,求有多少个 个点的有标号简单无向图(无自环重边)满足:
- 点 的度数为 ;
- 点 到点 有且仅有一条最短路;
- ,点 到点 的最短路长度大于等于点 到点 的;
对 取模。
,。
考虑 bfs,显然图可以分成若干层,每一层点的编号连续且 bfs 树上的横叉边都在同一层内。
那么不难想到设 表示处理完前 个点,最后一层往下连了 条边的方案数。
转移时下一层的区间一定是 ,且区间内去掉当前层的 条边就只剩下一度点和二度点。那么不难想到设 表示一层内有 个 度点和 个二度点,往下一层连了 条边的内部连边方案数。则有:
其中 和 表示 中 和 的个数。
但是发现这样会算重,因为二度点无论连哪边都是一样的。
所以若上一层有 个二度点是两边都往下连的,则转移时需要除掉 。
这个问题是好处理的,只需要把 放进 中即可。
现在来考虑 怎么求,首先显然有 。
不难发现一度点和二度点之间的顺序是没有关系的,所以不妨钦定先放一度点再放二度点。
下文 x
表示一度点,o
表示二度点,-
表示层内连边,+
表示向下一层的连边
对于没有二度点的情况,考虑最后一个一度点 :
u+
型:;x-u
型:;
对于有二度点的情况,考虑最后一个二度点 。为了避免算重, 多出来的边直接往下连:
+u+
型:,其中 是转移时的 ;x-u+
型:;-o-u+
型:连出去的二度点规约为一个一度点,;x-u-x
型:;x-u-o-
型:连出去的二度点规约为一个一度点,;-o-u-o-
型:两个二度点都规约为一度点,;
时间复杂度 ,空间复杂度 。
代码如下:
#include <iostream>
#include <cstdio>
using namespace std;
const int S=305,p=1000000007,inv2=(p+1)/2;
int fra[S];
int n,a[S];
int g[S][S][S],f[S][S];
inline void add(int &x,int y)
{
x+=y;
if(x>=p) x-=p;
}
inline int qpow(int x,int y)
{
int res=1;
for(;y>0;y>>=1,x=1ll*x*x%p) res=y&1?1ll*res*x%p:res;
return res;
}
int main()
{
fra[0]=1;
for(int i=1;i<=S-3;i++) fra[i]=1ll*fra[i-1]*i%p;
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
g[0][0][0]=1;
for(int k=0;k<=n;k++)
{
for(int j=0;j<=n&&j+k<=n;j++)
{
for(int i=0;i<=n&&i+j+k<=n;i++)
{
if(i+j*2<k) continue;
if(i+j+k==0) continue;
int &u=g[i][j][k];
if(j==0)
{
// u-
if(k>=1) add(u,g[i-1][j][k-1]);
// u-x
if(i>=2) add(u,1ll*g[i-2][j][k]*(i-1)%p);
}
else
{
// x-u-x
if(i>=2) add(u,1ll*g[i-2][j-1][k]*i%p*(i-1)%p*inv2%p);
// x-u-o- OR -o-u-x
if(i>=1&&j>=2) add(u,1ll*g[i][j-2][k]*i%p*(j-1)%p);
// -o-u-o-
if(j>=3) add(u,1ll*g[i+2][j-3][k]*(j-1)%p*(j-2)%p*inv2%p);
if(k>=1)
{
// x-u- OR -u-x
if(i>=1) add(u,1ll*g[i-1][j-1][k-1]*i%p);
// -o-u- OR -u-o-
if(j>=2) add(u,1ll*g[i+1][j-2][k-1]*(j-1)%p);
if(k>=2)
{
// -u-
add(u,1ll*g[i][j-1][k-2]*inv2%p);
}
}
}
}
}
}
f[1][a[1]]=qpow(fra[a[1]],p-2);
for(int i=1;i<=n-1;i++)
{
int c1=0,c2=0;
for(int j=i+1;j<=n;j++)
{
c1+=a[j]==2;
c2+=a[j]==3;
for(int k=0;k<=n&&k<=c1+c2*2;k++)
{
int pre=1ll*f[i][j-i]*g[c1][c2][k]%p*fra[j-i]%p;
add(f[j][k],pre);
}
}
}
printf("%d\n",f[n][0]);
return 0;
}