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元树的数目。
我是先从简单的情况入手,就讨论完全二叉树的个数,得到是2^2n -1
然后又想完全三叉树的个数
然后就陷入了一个误区:总是想考虑下面的叶子节点该怎么加才满足,使劲推公式尽量往组合数上靠,结果憋了半天没搞出来。
然后题解是dp:
定义f[i]表示深度为i的n元树的个数,发现这个并不好直接转移
因为组合的时候不一定是一些i-1深度的n元树。
需要转化:
改为求f[i]的前缀和s[i]
s[i]表示深度不大于i的n元树的个数.
这里转移不是考虑怎么在下面加节点,而是添一个根:
s[i-1]种不同的n元树,在添一个根时候的状态是n棵不同的不超过i-1深度的树排在一起,也有可能为空.
s[i]=s[i-1]^n +1(我们考虑树不为空的情况,但组合的时候可能只有根,而s[i-1]状态里没有空的子树)
然后答案就是s[i]-s[i-1]了。
这里吸取一个教训,不要局限于题意顺序,逆向思维。
#include<cstdio>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<queue>
#include<cmath>
#define base 10000
#define maxlen 101
#define get(x) (x-'0')
using namespace std;
int n,d;
const int maxn=20;
struct bign
{
int sign,len;
int c[maxlen];
bign()
{
sign=0;
len=1;
memset(c,0,sizeof(c));
}
void Printf()
{
if(sign)printf("-");
printf("%d",c[len]);
for(int i=len-1;i>=1;i--)printf("%04d",c[i]);
printf("\n");
}
void zero()
{
while(len-1&&!c[len])len--;
}
void writen(char *s)
{
int l=strlen(s),lim=0,k=1;
if(s[0]=='-')
{
sign=1;
lim=1;
}
for(int i=l-1;i>=lim;i--)
{
c[len]+=get(s[i])*k;
k*=10;
if(k==base)
{
len++;
k=1;
}
}
}
void Read()
{
char s[maxlen*base];
scanf("%s",s);
writen(s);
}
bool operator <(const bign &b)
{
if(len!=b.len)return len<b.len;
for(int i=len;i>=1;i--)
{
if(c[i]!=b.c[i])return c[i]<b.c[i];
}
return false;
}
bign operator =(const int &a)
{
char s[maxlen*base];
sprintf(s,"%d",a);
writen(s);
return *this;
}
bign operator +(const bign &b)
{
bign r;
r.len=max(len,b.len)+1;
for(int i=1;i<=r.len;i++)
{
r.c[i]+=c[i]+b.c[i];
r.c[i+1]+=r.c[i]/base;
r.c[i]%=base;
}
r.zero();
return r;
}
bign operator +(const int &a)
{
bign b;
b=a;
return *this+b;
}
bign operator *(const bign &b)
{
bign r;
r.len=len+b.len+2;
for(int i=1;i<=len;i++)
{
for(int j=1;j<=b.len;j++)
{
r.c[i+j-1]+=c[i]*b.c[j];
r.c[i+j]+=r.c[i+j-1]/base;
r.c[i+j-1]%=base;
}
}
r.zero();
return r;
}
bign operator *(const int &a)
{
bign b;
b=a;
return *this*b;
}
bign operator ^(const int &y)
{
bign res;
bign r=*this;
res=1;
int t=y;
while(t)
{
if(t&1)res=res*r;
t>>=1;
r=r*r;
}
return res;
}
bign operator -(const bign &b)
{
bign r=*this;
bign y=b;
if(r<y)swap(r,y);
for(int i=1;i<=r.len;i++)
{
r.c[i]-=y.c[i];
if(r.c[i]<0)
{
r.c[i]+=base;
r.c[i+1]--;
}
}
return r;
}
}f[maxn];
//f[i]表示深度不超过i的n元树的个数
int main()
{
scanf("%d%d",&n,&d);
//f[i]=f[i-1]^n+1
f[0]=1;
for(int i=1;i<=d;i++)f[i]=(f[i-1]^n)+1;
if(d==0)printf("1\n");
else
{
bign ans=f[d]-f[d-1];
ans.Printf();
}
return 0;
}