SCOI2003【严格n元树】

来源:互联网 发布:一键安装java环境 编辑:程序博客网 时间:2024/06/05 15:46

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+1f[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;}


1 0