[洛谷P4492] HAOI2018 苹果树

问题描述

小 C 在自己家的花园里种了一棵苹果树, 树上每个结点都有恰好两个分支. 经过细心的观察, 小 C 发现每一天这棵树都会生长出一个新的结点.

第一天的时候, 果树会长出一个根结点, 以后每一天, 果树会随机选择一个当前树中没有长出过结点 的分支, 然后在这个分支上长出一个新结点, 新结点与分支所属的结点之间连接上一条边.

小 C 定义一棵果树的不便度为树上两两结点之间的距离之和, 两个结点之间 的距离定义为从一个点走到另一个点的路径经过的边数.

现在他非常好奇, 如果 N 天之后小 G 来他家摘苹果, 这个不便度的期望 E 是多少. 但是小 C 讨厌分数, 所以他只想知道 (E imes N !)(P) 取模的结果, 可以证明这是一个整数.

输入格式

从标准输入中读入数据. 一行两个整数 (N, P) .

输出格式

输出到标准输出中. 输出一个整数表示答案.

样例输入

3 610745795

样例输出

24

数据范围

(N le 2000,Ple 10^9+7)

解析

老套路题了。

不难发现,第 N 天的树上一定有 N+1 个分支可以长结点。因此,大小为 N 的满足要求的树有 N! 中方法。

直接统计十分复杂,我们考虑计算每条边的贡献。设当前枚举的是节点 (i) 的父边,其子树大小为 (j) 。那么,在确定树的形态的情况下,这条边会被经过 (j(n-j)) 次。

接下来分别考虑子树和子树外的形态。对于子树,由之前的结论,有 (j!) 种不同的形态。而考虑编号的问题,子树内的编号一定都大于 (i) 。所以总方案数为 (j!C_{n-i}^{j-1}) 。对于子树外,首先在前 (i) 天时就有 (i!) 中形态。在第 (i) 天之后,除了钦定的在子树内的点以外,其余的每一个点都可以放在子树外的一个分支上,依次有 (i+1-2...n-j-1) 种方案(因为点放在子树内导致的分支增加对子树外没有影响,毕竟不能放上去)。综上,总贡献为

[j(n-j) imes j!C_{n-i}^{j-1} imes i(i-1)(n-j-1)! ]

枚举 (i)(j) 即可。

代码

#include <iostream>
#include <cstdio>
#define int long long
#define N 2002
using namespace std;
int n,mod,i,j,c[N][N],fac[N],ans;
signed main()
{
	scanf("%lld%lld",&n,&mod);
	for(i=fac[0]=1;i<=n;i++) fac[i]=fac[i-1]*i%mod;
	c[0][0]=1;
	for(i=1;i<=n;i++){
		c[i][0]=1;
		for(j=1;j<=i;j++) c[i][j]=(c[i-1][j-1]+c[i-1][j])%mod;
	}
	for(i=1;i<=n;i++){
		for(j=1;j<=n-i+1;j++){
			int tmp=fac[j]*c[n-i][j-1]%mod*i%mod*(i-1)%mod*fac[n-j-1]%mod*j%mod*(n-j)%mod;
			ans=(ans+tmp)%mod;
		}
	}
	printf("%lld
",ans);
	return 0;
}
原文地址:https://www.cnblogs.com/LSlzf/p/13388113.html