【题解】[scoi2003]严格n元树

本文介绍了一种计算特定深度的严n元树数量的方法,通过动态规划结合高精度运算实现了有效求解,并提供了完整的代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Description

如果一棵树的所有非叶节点都恰好有n个儿子,那么我们称它为严格n元树。如果该树中最底层的节点深度为d(根的深度为0),那么我们称它为一棵深度为d的严格n元树。例如,深度为2的严格2元树有三个,如下图: 给出n, d,编程数出深度为d的n元树数目。

Input

仅包含两个整数n, d( 0   <   n   <   =   32,   0  < =   d  < = 16)

Output

仅包含一个数,即深度为d的n元树的数目。

Sample Input

【样例输入1】
2 2

【样例输入2】
2 3

【样例输入3】
3 5

Sample Output

【样例输出1】
3

【样例输出2】
21

【样例输出2】

58871587162270592645034001

-----------------------------------------------------------------------------------------------

感觉这道题想树形dp+组合数。。纠结了一会没想到简单点的写法

于是参照了大神们的题解。。。发现这道题是如此。。。。

设dp[i]为深度不大于i的严格n元树的个数。自然dp[0]=1,答案就为dp[d]-dp[d-1]。

假设我们已知dp[i-1],那么深度不大于i的树就相当于多出了一个根节点,其每一个儿子(共n个儿子)都是一颗深度不大于i-1的树。

那么dp[i]=dp[i-1]^n+1(还有没有儿子的情况),直接线性递推就可以得到答案了。

这里还有一个坑点:当d==0的时候,ans直接=dp[0]而不是dp[0]-dp[0-1];这里数组超界不一定会RE,而有可能wa。。。我就在这被坑了好久

另一种推法参见这位神牛的题解:   http://hi.baidu.com/gzh19950711/item/c3ac0f0b0ad3b0c190571881

这题另外的考点在于高精度和快速幂。。。(其实不加快速幂大概也能过吧- -)

代码:

#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<algorithm>
#include<cstring>
#include<iostream>
using namespace std;
const int M=1000;
struct bign
{
	int num[1000+10],len;
	bign()
	{
		memset(num,0,sizeof(num));
		len=1;
	}
	bign operator =(int x)
	{
		len=0;
		while(x)
		{
			num[len++]=x%M;
			x/=M;
		}
		if(len==0)len=1;
		return *this;
	}
	bign operator =(const bign &b)
	{
		len=b.len;
		for(int i=0;i<len;i++)num[i]=b.num[i];
		return *this;
	}
	bign operator +(int x)
	{
		bign c;
		c=*this;
		c.num[0]=c.num[0]+x;
		int temp=c.num[0]/M;
		c.num[0]=c.num[0]%M;
		int i=1;
		while(temp)
		{
			c.num[i]=c.num[i]+temp;
			temp=c.num[i]/M;
			c.num[i]=c.num[i]%M;
			i++;
		}
		if(i>c.len)c.len=i;
		return c;
	}
	bign operator *(int x)
	{
		bign c;
		c.len=len;
		for(int i=0;i<c.len;i++)c.num[i]=num[i]*x;
		int temp=0;
		for(int i=0;i<c.len;i++)
		{
			c.num[i]+=temp;
			temp=c.num[i]/M;
			c.num[i]=c.num[i]%M;
			if(i==c.len-1&&temp!=0)c.len++;
		}
		while(c.len&&c.num[c.len-1]==0)c.len--;
		if(c.len==0)c.len=1;
		return c;
	}
	bign operator *(const bign &b)
	{
		bign c;
		c.len=len+b.len-1;
		for(int i=0;i<len;i++)
		{
			for(int j=0;j<b.len;j++)
			{
				c.num[i+j]+=num[i]*b.num[j];
			}
		}
		int temp=0;
		for(int i=0;i<c.len;i++)
		{
			c.num[i]+=temp;
			temp=c.num[i]/M;
			c.num[i]=c.num[i]%M;
			if(i==c.len-1&&temp!=0)c.len++;
		}
		while(c.len&&c.num[c.len-1]==0)c.len--;
		if(c.len==0)c.len=1;
		return c;
	}
	bign operator -(const bign &b)
	{
		bign c;
		c.len=len;
		int temp=0;
		for(int i=0;i<c.len;i++)
		{
			if(num[i]-temp<b.num[i])
			{
				c.num[i]=num[i]-temp+M-b.num[i];
				temp=1;
			}
			else
			{
				c.num[i]=num[i]-temp-b.num[i];
				temp=0;
			}
		}
		while(c.len&&c.num[c.len-1]==0)c.len--;
		if(c.len==0)c.len=1;
		return c;
	}
	void print()
	{
		printf("%d",num[len-1]);
		for(int i=len-2;i>=0;i--)
		{
			printf("%03d",num[i]);
		}
		printf("\n");
	}
};
int n,d;
bign qpow(const bign &x,int c)
{
	bign ret;
	if(c==0)return ret=1;
	if(c==1)return ret=x;
	ret=qpow(x,c/2);
	ret=ret*ret;
	if(c&1)ret=ret*x;
	return ret;
}
bign dp[20];
bign ans;
int main()
{
	scanf("%d%d",&n,&d);
	dp[0]=1;
	for(int i=1;i<=d;i++)
	{
		dp[i]=qpow(dp[i-1],n)+1;
	}
	if(d>0)ans=dp[d]-dp[d-1];
	else ans=dp[d];
	ans.print();
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值