小 C 在自己家的花园里种了一棵苹果树, 树上每个结点都有恰好两个分支. 经过细心的观察, 小 C 发现每一天这棵树都会生长出一个新的结点.
第一天的时候, 果树会长出一个根结点, 以后每一天, 果树会随机选择一个当前树中没有长出过结点 的分支, 然后在这个分支上长出一个新结点, 新结点与分支所属的结点之间连接上一条边.
小 C 定义一棵果树的不便度为树上两两结点之间的距离之和, 两个结点之间 的距离定义为从一个点走到另一个点的路径经过的边数.
现在他非常好奇, 如果 \(N\) 天之后小 G 来他家摘苹果, 这个不便度的期望 \(E\) 是多少. 但是小 C 讨厌分数, 所以他只想知道 \(E \times N!\)对 \(P\) 取模的结果, 可以证明这是一个整数.
题解
新建一个节点之后,会减少一个空位,增加2个空位,因此空位数加1
所以节点数为 \(n\) 的树的方案数为 \(1\times 2\times \dots \times n=n!\)
那个所有点的距离之和,考虑每个点和它父亲之间的边的贡献,设 \(siz\) 为它的子树大小。
答案为 \(siz\times (n-siz)\)
考虑枚举点 \(i\) 和 \(siz\) ,下面问题转化为求合法的树的方案数。
首先构造出 \(i\) 个点的树,方案数为 \(i!\)
然后把 \(siz-1\) 个点挂到 \(i\) 下面,要求这些点标号都大于 \(i\) ,方案数为 \(C_{n-i}^{siz-1}\)
其他点的方案数?还剩下 \(n-i-siz+1\) 个点。
我们假设那 \(siz-1\) 个点还没有挂上去, \(i\) 下面有两个空位,但是都不能放,因此方案数为 \(\prod_{k=1}^{n-i-siz+1}(i+k-2)\)
把上面的方案数乘起来,整理一下,得到
\]
复杂度 \(O(n^2)\)
#include<stdio.h>
#include<cctype>
#include<algorithm>
#define REP(i,a,b) for(int i(a);i<=(b);++i)
#define dbg(...) fprintf(stderr,__VA_ARGS__)
template<typename T,typename U>inline bool smin(T&x,const U&y){return x>y?x=y,1:0;}
template<typename T,typename U>inline bool smax(T&x,const U&y){return x<y?x=y,1:0;}
const int N=2005;
int n,p,C[N][N],fac[N];
int main(){
scanf("%d%d",&n,&p);
C[0][0]=fac[0]=1;
REP(i,1,n){
C[i][0]=1;fac[i]=1ll*fac[i-1]*i%p;
REP(j,1,n)if((C[i][j]=C[i-1][j]+C[i-1][j-1])>=p)C[i][j]-=p;
}
int ans=0;
REP(i,2,n)REP(j,1,n-i+1)ans=(ans+1ll*i*j*(i-1)%p*fac[n-j]%p*fac[j]%p*C[n-i][j-1])%p;
printf("%d\n",ans);
return 0;
}