题目
分析
cjb的题解已经很详细了。下面是本人理解。
算法的思想是从一个核心开始往外扩展,用
f
i
,
j
f_{i,j}
fi,j表示有
j
j
j个叶子的
i
i
i标号树的个数。然后加叶子时枚举增加的叶子
k
k
k即可转移到
f
i
+
k
,
k
f_{i+k,k}
fi+k,k上了。接着再引入
g
i
,
j
,
k
g_{i,j,k}
gi,j,k表示新加入的叶子的序列方案。于是可以知道如何转移了
f
i
+
k
,
k
=
f
i
,
j
⋅
g
i
,
j
,
k
⋅
C
i
+
k
i
f_{i+k,k}=f_{i,j}·g_{i,j,k}·C^{i}_{i+k}
fi+k,k=fi,j⋅gi,j,k⋅Ci+ki
然后就是求
g
g
g数组了。
对于数组中的第
k
k
k个节点,如果他是目前没有子节点的旧叶节点,那么就有
i
i
i种方案,即
i
⋅
g
i
,
j
,
k
−
1
i·g_{i,j,k-1}
i⋅gi,j,k−1。如果不是,那么方案数即为
j
⋅
g
i
−
1
,
j
−
1
,
k
−
1
j·g_{i-1,j-1,k-1}
j⋅gi−1,j−1,k−1。
有了
f
,
g
f,g
f,g数组,最后就是求答案了。这里引入
h
i
,
j
h_{i,j}
hi,j表示有
j
j
j个叶子的
i
i
i标号树的直径和。
将
h
i
,
j
h_{i,j}
hi,j转移到
h
i
+
k
,
i
h_{i+k,i}
hi+k,i要注意新加的节点自身的贡献以及对老树的贡献。于是可以得到
h
[
i
+
k
]
[
k
]
=
h
[
i
+
k
]
[
k
]
+
h
[
i
]
[
j
]
∗
g
[
i
]
[
j
]
[
k
]
∗
C
[
i
+
k
]
[
i
]
+
2
∗
f
[
i
]
[
j
]
∗
C
[
i
+
k
]
[
k
]
∗
g
[
i
]
[
j
]
[
k
]
h[i+k][k]=h[i+k][k]+h[i][j]*g[i][j][k]*C[i+k][i]+2*f[i][j]*C[i+k][k]*g[i][j][k]
h[i+k][k]=h[i+k][k]+h[i][j]∗g[i][j][k]∗C[i+k][i]+2∗f[i][j]∗C[i+k][k]∗g[i][j][k]
最后
a
n
s
=
∑
i
=
1
n
h
n
,
i
ans=\sum_{i=1}^nh_{n,i}
ans=i=1∑nhn,i
时间复杂度是
O
(
n
3
)
O(n^3)
O(n3)
#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll n,p,ans;
ll h[505][505],f[505][505],g[505][505][505];
ll C[505][505];
int main()
{
scanf("%lld%lld",&n,&p);A
for (ll i=0;i<=500;i++) C[i][0]=C[i][i]=1;
for (ll i=2;i<=500;i++)
for (ll j=1;j<i;j++)
C[i][j]=(C[i-1][j]+C[i-1][j-1])%p;
g[0][0][0]=1;
for (ll i=1;i<=n;i++)
{
ll sum=1;
for (ll k=0;k<=n;k++)
{
g[i][0][k]=sum;
sum=sum*i%p;
}
}
for (ll i=1;i<=n;i++)
for (ll j=1;j<=i;j++)
for (ll k=j;k<=n-i;k++)
g[i][j][k]=(i*g[i][j][k-1]%p+j*g[i-1][j-1][k-1]%p)%p;
f[1][1]=f[2][2]=1;
for (ll i=1;i<=n;i++)
for (ll j=1;j<=i;j++)
for (ll k=j+(i==1?1:0);k<=n-i;k++)
f[i+k][k]=(f[i+k][k]+f[i][j]*g[i][j][k]%p*C[i+k][i]%p)%p;
h[2][2]=1;
for (ll i=1;i<=n;i++)
for (ll j=1;j<=i;j++)
for (ll k=j+(i==1?1:0);k<=n-i;k++)
h[i+k][k]=((h[i+k][k]+h[i][j]*g[i][j][k]%p*C[i+k][i]%p)%p+2*f[i][j]%p*C[i+k][k]%p*g[i][j][k]%p)%p;
for (ll i=1;i<=n;i++)
ans=(ans+h[n][i])%p;
printf("%lld\n",ans);
}
ps:记得取模。