JZOJ7月17日提高组T1 亲戚

题目

Description

在这里插入图片描述

Input

在这里插入图片描述

Output

在这里插入图片描述

Sample Input

4
0 1 1 0

Sample Output

8

Data Constraint

在这里插入图片描述

题解

分析

题意:排序,其中每个节点必须在它的父亲之后

方法1

设一个虚拟根(如0)
使数据变成一棵
先思考倒数第2层
那么这时候,不考虑当前节点
子节点的排序方法可以这样算:
当一个子节点进来时
判断是否为第一个子节点
如果不是,那么这个子节点就可以插入在已经进来的所有子节点的左右两侧
如图:
在这里插入图片描述
接下来思考剩下层
对于倒数第3层
这时以当前节点为根的树就不止一个节点了
那么这时候,就要用到组合数
设已经进入的儿子节点贡献的节点个数为 n u m [ n o w ] num[now] num[now],将要进来的节点个数为 n u m [ x ] num[x] num[x],那么有公式: f [ n o w ] = C n u m [ n o w ] + n u m [ x ] n u m [ x ] ∗ f [ x ] ( f [ n o w ] 表 示 当 前 节 点 合 法 的 方 案 数 , x 是 n o w 的 儿 子 节 点 ) f[now]=C^{num[x]}_{num[now]+num[x]}*f[x](f[now]表示当前节点合法的方案数,x是now的儿子节点) f[now]=Cnum[now]+num[x]num[x]f[x]f[now]xnow
对于组合数,可以通过逆元来计算
我们知道: C y x = x ! y ! ∗ ( x − y ) ! C^x_y=\dfrac{x!} {y!*(x-y)!} Cyx=y!(xy)!x!
PS:先预处理出阶乘
计算 x ! y ! ( x − y ) ! \dfrac{x!}{y!(x-y)!} y!(xy)!x!,如果暴力计算C++没有类型存的下(如果暴力模数可能会导致答案出错)
那么这时逆元就可以用起来了
求逆元有很多种方法
例如:费马小定理


以下是费马小定理如何求逆元:
我们知道,当 a ∗ x ≡ 1 ( m o d a*x≡1(mod ax1(mod P ) P) P)时,称 x x x a a a在模 P P P下的逆元(反过来也行)
同时又有费马小定理:当 P P P是质数时,有 a P − 1 ≡ 1 ( m o d a^{P-1}≡1(mod aP11(mod P ) P) P),而且显而易见 a P − 1 = a ∗ a P − 2 a^{P-1}=a*a^{P-2} aP1=aaP2
所以 a P − 1 ≡ 1 ( m o d a^{P-1}≡1(mod aP11(mod P ) P) P)可以转换为 a ∗ a P − 2 ≡ 1 ( m o d a*a^{P-2}≡1(mod aaP21(mod P ) P) P)
结合 a ∗ x ≡ 1 ( m o d a*x≡1(mod ax1(mod P ) P) P) a ∗ a P − 2 ≡ 1 ( m o d a*a^{P-2}≡1(mod aaP21(mod P ) P) P),可以发现
在模 P P P的意义下时, a P − 2 a^{P-2} aP2就是 a a a的逆元
a P − 2 a^{P-2} aP2通常用快速幂来求
注意,费马小定理推逆元仅当P是质数时才适用!!!


求出组合数后,依次传递
最后输出 f [ 0 ] f[0] f[0]
结合代码理解

方法2

对于答案,其实有公式: a n s = n ! ∏ i = 1 n S i z e i ans=\dfrac {n!} {\prod _{i=1}^{n}Size_i} ans=i=1nSizein!
其中 S i z e i Size_i Sizei表示以i为根节点的子树的点的个数
为什么呢?
证明过程
膜拜HQX

Code(方法1)

#include<cstdio>
#define mod 1000000007
using namespace std;
int n,i,x,tot,num[200005];
long long f[200005],jc[200005];
struct node
{
	int to,head,next;
}a[200005];
long long ksm(long long x,long long y)
{
	long long s;
	s=1;
	while (y>0)
	{
		if (y&1) s=s*x%mod;
		y>>=1;
		x=x*x%mod;
	}
	return s;
}
long long Cc(int x,int y)
{
	long long Ksm=ksm(jc[y]*jc[x-y]%mod,mod-2)%mod;
	long long ccc=jc[x]*Ksm%mod;
	return ccc%mod;
}
void dfs(int now)
{
	int i,x;
	long long c;
	for (i=a[now].head;i!=0;i=a[i].next)
	{
		x=a[i].to;
		dfs(x);
		if (num[now]!=0)
		{
			c=Cc(num[now]+num[x],num[x]);
			f[now]=f[now]*c%mod;
		}
		num[now]+=num[x];
		f[now]=f[now]*f[x]%mod;
	}
	num[now]++;
}
int main()
{
	freopen("input.in","r",stdin);
	scanf("%d",&n);
	f[0]=1;
	for (i=1;i<=n;i++)
	{
		scanf("%d",&x);
		tot++;
		a[tot].to=i;
		a[tot].next=a[x].head;
		a[x].head=tot;
		f[i]=1;
	}
	jc[0]=1;
	for (i=1;i<=n;i++)
		jc[i]=jc[i-1]*(long long)i%mod;
	dfs(0);
	printf("%lld\n",f[0]);
	return 0;
}

抱歉只有方法1的Code,毕竟方法2的代码量很小,可以自己试着理解码出来

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值