题意
给定长度为 (n) 的序列 (d),求出 (n) 个点的无向无权图数量,满足
-
每个点到 (1) 的最短路存在且唯一。
-
设 (l_i) 为 (i) 到 (1) 的最短路,要保证对于所有 (2leq ileq n) 有 (l_igeq l_{i-1}) 成立。
-
(i) 号点的度数为 (d_i)。
( exttt{Data Range:}1leq nleq 300,2leq d_ileq 3)
题解
神仙题。
( exttt{CF}) 上的数据比较水,(n) 只有 (50),以题意上的数据范围为准。
首先证明一个结论:符合要求的图的边集肯定能被划分成原图的 ( exttt{bfs}) 树加上一些同深度的点连的边。
首先抠出原图的一棵 ( exttt{bfs}) 树,考虑某条非树边 ((u,v)) 与 (1) 到 (u) 和 (1) 到 (v) 的距离构成的环的长度 (l) 的奇偶性,那么有
-
如果这个环是偶环,那么存在一个点 (w) 使得它走树边到 (1) 的距离为 (frac{l}{2})。注意到这个图是无权图,所以最短路树就是 ( exttt{bfs}) 树,即 (operatorname{dist}(w,1)=frac{l}{2})。
注意到这个环的长度是 (l),那么 (w) 经过非树边走到 (1) 的距离是 (l-frac{l}{2}=frac{l}{2}=operatorname{dist}(w,1)),所以最短路不唯一,矛盾。
-
如果这个环是个奇环,那么不妨设 (u) 是深度较小的那个点。
如果 (dep_u eq dep_v) 的话,那么 (dep_vgeq dep_u+2),此时有两条路径到达 (v)。其中先走树边到达 (u) 再到达 (v) 的距离为 (dep_u+1),而直接走树边走到 (v) 的距离为 (dep_v)。很显然,前者更短,但是根据最短路树,后者是 (1) 到 (v) 的“最短路”,矛盾。
所以如果 (u) 和 (v) 不是同一深度的点那么怎么样都能推出矛盾。
接着考虑 ( exttt{dp})。设 (f_{i,j}) 表示已经考虑了前 (i) 个点,最后 (j) 个点与第 (i) 个点在同一层的方案数。
同时设 (g_{i,j,k}) 表示这一层有 (i) 个点,上一层度数为 (2) 的点有 (j) 个,度数为 (3) 的点有 (k) 个,这一层与上一层的树边和上一层的非树边的方案数。
根据定义我们可以得到答案为 (sumlimits_{i=1}^{n-1}f_{n,i}g_{0,c_0,c_1}),其中 (c_0) 和 (c_1) 分别为最后 (i) 个点中度数为 (2) 和 (3) 的点的数量。
考虑如何转移 (f),枚举上一层有多少个点,我们可以得到这样的式子:
其中 (c_0) 和 (c_i) 分别为从 (i) 开始倒着数 (k) 个点中度数为 (2) 和 (3) 的点的数量。
注意到这个式子是 (O(n^3)) 的,如果 (g) 也可以 (O(n^3)) 转移的话那就万事大吉了。
考虑分情况讨论一下,首先有初始状态 (g_{0,0,0}=1)。
- 如果 (i=j=0,k eq 0) 的话,那么所有的上层节点都是 (3) 度的。除去往父亲连的边之外,每一个点都有两条边连向同一层的点,所以构成了一些环。枚举一下环的大小(不能等于 (2))就可以写出以下式子(这里平移了下标):
- 如果 (i=0,j eq 0) 的话,考虑在上一层找个 (2) 度点,那么通过把他拿掉并且讨论同一层连的那个点的度数的方法得到这个式子:
- 如果 (i eq 0) 的话,类似上面的思路,在这一层找一个点,我们得到:
这里后面那个 (g) 的 (j) 要 (+1) 的原因是减少了一个 (3) 度的点,但是产生了一个 (2) 度的点。
发现这个东西是 (O(n^3)) 的,于是总复杂度也是 (O(n^3)),可以通过。
下面的代码可以在 LOJ 上通过,但是在 CF 上会被卡空间,解决方法是将 MAXN
改成 (51) 即可。
代码
#include<bits/stdc++.h>
using namespace std;
typedef int ll;
typedef long long int li;
const ll MAXN=551,MOD=1e9+7,INV2=5e8+4;
ll n,res;
ll fact[MAXN],finv[MAXN],d[MAXN],f[MAXN][MAXN],g[MAXN][MAXN][MAXN];
ll c[2];
inline ll read()
{
register ll num=0,neg=1;
register char ch=getchar();
while(!isdigit(ch)&&ch!='-')
{
ch=getchar();
}
if(ch=='-')
{
neg=-1;
ch=getchar();
}
while(isdigit(ch))
{
num=(num<<3)+(num<<1)+(ch-'0');
ch=getchar();
}
return num*neg;
}
inline ll qpow(ll base,ll exponent)
{
ll res=1;
while(exponent)
{
if(exponent&1)
{
res=(li)res*base%MOD;
}
base=(li)base*base%MOD,exponent>>=1;
}
return res;
}
inline void setup(ll cnt)
{
fact[0]=fact[1]=finv[0]=1;
for(register int i=2;i<=cnt;i++)
{
fact[i]=(li)fact[i-1]*i%MOD;
}
finv[cnt]=qpow(fact[cnt],MOD-2);
for(register int i=cnt-1;i;i--)
{
finv[i]=(li)finv[i+1]*(i+1)%MOD;
}
}
inline ll binom(ll m,ll n)
{
return (li)fact[m]*finv[n]%MOD*finv[m-n]%MOD;
}
inline void calc()
{
ll x;
g[0][0][0]=1;
for(register int i=0;i<=n;i++)
{
for(register int j=0;j<=n-i;j++)
{
if(i==0&&j==0)
{
continue;
}
if(!i)
{
for(register int k=2;k<j;k++)
{
x=(li)g[0][i][j-k-1]*binom(j-1,k)%MOD*fact[k]%MOD*INV2%MOD;
g[0][i][j]=(g[0][i][j]+x)%MOD;
}
}
else
{
if(i>=2)
{
g[0][i][j]=(g[0][i][j]+(li)(i-1)*g[0][i-2][j]%MOD)%MOD;
}
if(j>=1)
{
g[0][i][j]=(g[0][i][j]+(li)j*g[0][i][j-1]%MOD)%MOD;
}
}
}
}
for(register int i=1;i<n;i++)
{
for(register int p=1,j;p<n-i;p++)
{
for(register int k=0;k<=p;k++)
{
j=p-k;
if(j)
{
g[i][j][k]=(g[i][j][k]+(li)j*g[i-1][j-1][k]%MOD)%MOD;
}
if(k)
{
g[i][j][k]=(g[i][j][k]+(li)k*g[i-1][j+1][k-1]%MOD)%MOD;
}
}
}
}
}
int main()
{
setup(n=read()),calc();
for(register int i=1;i<=n;i++)
{
d[i]=read();
}
f[d[1]+1][d[1]]=1;
for(register int i=d[1]+2;i<=n;i++)
{
for(register int j=1;j<=i-d[1]-1;j++)
{
c[0]=c[1]=0;
for(register int k=1;k<i-j;k++)
{
c[d[i-j-k+1]==3]++;
f[i][j]=(f[i][j]+(li)g[j][c[0]][c[1]]*f[i-j][k]%MOD)%MOD;
}
}
}
c[0]=c[1]=0;
for(register int i=1;i<n;i++)
{
c[d[n-i+1]==3]++;
res=(res+(li)f[n][i]*g[0][c[0]][c[1]]%MOD)%MOD;
}
printf("%d
",res);
}