思路
题目要求的其实就是每种方案的权值之和(因为每种方案的概率相等)
所以自然想到要求所有的边对最终答案的贡献次数
考虑这一条边被经过了多少次,有这个子树内的点数*子树外的点数次,即(k imes(n-k))
然后考虑总共的中序遍历总共有(n!)种,每种方案等概率
先钦定一个点(i)(乘上(i!)),然后枚举它的(sz),这样相当于枚举了每种生成的树的形态,做到了不重不漏
对于这个点(i),考虑选择K个点作为它的子树进行统计((sz_i=K+1)),然后选择的k个点的方案数是(left(egin{matrix}n-i\Kend{matrix} ight)),这(K)个点能构成的树的形态总数有(K!)个,然后考虑剩下的(n-k-i)个点的分配方案,因为不能放在(i)的子树中,所以分配的方案有((i-1)(i)dots(n-K-2))种((n-K-2)因为只剩一个点,能选择(n-sz_i-1=n-K-2)个点),化简一下等于(frac{(n-k-2)!}{(i-2)!})
然后式子就出来了(注意i从二开始,因为从1开始似乎没有什么意义)
[egin{align}ans=&sum_{i=2}^ni!sum_{j=1}^{n-i+1}j!left(egin{matrix}n-i\j-1end{matrix}
ight)frac{(n-j-1)!}{(i-2)!}j(n-j)\=&sum_{i=2}^nsum_{j=1}^{n-i+1}j!left(egin{matrix}n-i\j-1end{matrix}
ight)(n-j-1)!(i-1)ij(n-j)end{align}
]
然后就很可做了
代码
#include <cstdio>
#include <algorithm>
#include <cstring>
#define int long long
using namespace std;
int n,p,C[2010][2010],jc[2010],ans=0;
signed main(){
scanf("%d %d",&n,&p);
jc[0]=1;
for(int i=1;i<=n;i++)
jc[i]=(jc[i-1]*i)%p;
C[0][0]=1;
for(int i=1;i<=n;i++)
C[i][0]=C[i][i]=1;
for(int i=1;i<=n;i++)
for(int j=1;j<i;j++)
C[i][j]=(C[i-1][j-1]+C[i-1][j])%p;
for(int i=2;i<=n;i++)
for(int j=1;j<=n-i+1;j++)
ans=(ans+jc[j]*C[n-i][j-1]%p*jc[n-j-1]%p*(i-1)%p*i%p*j%p*(n-j)%p+p)%p;
printf("%d
",ans);
return 0;
}