[HAOI2018]苹果树(组合数学)

洛谷题目传送门

题目描述

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

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

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

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

题意简介

给定n,定义一个二叉树的权值为树上所有点对的最短距离和,求大小为n 的所有带标号二叉树的权值和。

解题思路

首先考虑n个点的无标号二叉树的构造方案数(左右儿子不同)
当n=1时,方案数为1
在这里插入图片描述
当n=2时,方案数为2(空心表示可选节点)
在这里插入图片描述
当n=3时,方案数为 2 × 3 = 6 2\times3=6 2×3=6
在这里插入图片描述
因此,一颗n各节点的树构造方案数是 n ! n! n!
然后开始计算答案,设当前第i个节点,该节点子树大小为j(包含i节点)
则i与i的父亲的连边对答案的贡献为 j × ( n − j ) j\times(n-j) j×(nj),也就是每一条经过这条边的路径都会产生贡献
构造之前i各节点的方案数为i!,构造子树的方案数为j!,但是因为有标号,所以子树的标号要从(n-i)里选 j-1 个(不算 i 节点),也就是 C n − i j − 1 C_{n-i}^{j-1} Cnij1
最后一部分是生成i+j-1后面的节点,并且这些节点不能放在i的子树中,所以这些节点中的第一个的方案数为 i-1,第2个为 i ,最后一个 是(n-j-1),总体方案数是
也就是 ( n − j − 1 ) ! ( i − 2 ) ! \frac{(n-j-1)!}{(i-2)!} (i2)!(nj1)!
把答案乘起来,也就是
∑ i = 1 n ∑ j = 1 n − j + 1 j ( n − j ) j ! C n − i j − 1 i ! ( n − j − 1 ) ! ( i − 2 ) ! \sum_{i=1}^{n}\sum_{j=1}^{n-j+1}j(n-j)j!C_{n-i}^{j-1}i!\frac{(n-j-1)!}{(i-2)!} i=1nj=1nj+1j(nj)j!Cnij1i!(i2)!(nj1)!
但是题目没保证模数p为质数,因此逆元可能没有,所以我们继续化简得
∑ i = 1 n ∑ j = 1 n − j + 1 j ( n − j ) j ! C n − i j − 1 ( n − j − 1 ) ! i ( i − 1 ) \sum_{i=1}^{n}\sum_{j=1}^{n-j+1}j(n-j)j!C_{n-i}^{j-1}{(n-j-1)!}i(i-1) i=1nj=1nj+1j(nj)j!Cnij1(nj1)!i(i1)
算组合数也不能用逆元, n 2 n^2 n2递推计算就可以了

#include<bits/stdc++.h>
using namespace std; 
typedef long long LL;
const int N = 2010;
LL n,p,ans;
LL C[N][N],fact[N];
void prework()
{
	fact[0]=1;
	for(int i=1;i<=n;i++)
	fact[i]=fact[i-1]*i%p;
	for(int i=0;i<=n;i++)
	C[i][0]=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;
		}
	}
}
int main()
{
	scanf("%d%d",&n,&p);
	prework();
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=n-i+1;j++)
		{
			ans=(ans+j*(n-j)%p*fact[j]%p*C[n-i][j-1]%p*fact[n-j-1]%p*i%p*(i-1)%p)%p;
		}
	}
	cout<<ans;
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值