首页 > 代码库 > 【HDU 5370】 Tree Maker(卡特兰数+dp)
【HDU 5370】 Tree Maker(卡特兰数+dp)
Tree Maker
Problem DescriptionTree Lover loves trees crazily.
One day he invents an interesting game which is named Tree Maker.
In this game, all trees are binary trees.
Initially, there is a tree with only one vertex and a cursor on it. Tree Lover can control the cursor to apply 5 operations to build a tree, and their formats are following:
0 : Jump to the parent of the current vertex.
1 : Jump to the left child of the current vertex.
2 : Jump to the right child of the current vertex.
3 x : Generate a tree with x vertices arbitrarily and make it the left subtree of the current vertex.
4 x : Generate a tree with x vertices arbitrarily and make it the right subtree of the current vertex.
When applying an operation, the log system will log down a record of it.
Tree Lover played this game for a whole day yesterday. As a forgetful man, although Tree Lover knew the shape of the tree while playing, after a sleep he forgot it.
All he has now is the logs of operations.
Tree Lover wants to know: how many possible shapes of the tree can have yesterday according to the logs?
Can you answer this question?
InputThe input consists of multiple test cases.
For each test case:
The first line is an integer n (1 <= n <= 500), denoting the lines of logs.
Then follow n lines of logs. The formats of logs are as described above.
The integer x of operation 3 and 4 is positive.
In each case, the number of vertices of the tree will never exceed 500.
You can assume that the cursor will never jump to a non-existent vertex.
If the left child of a vertex exists, operation 3 will not be applied on this vertex, and operation 4 is similar.
OutputFor each test case, ouput a single line “Case #x: y”, where x is the case number, starting from 1, and y is the answer to Tree Lover’s question.
Because the answer can be large, please output the answer mod 1000000007.
Sample Input23 34 323 31
Sample OutputCase #1: 25Case #2: 5HintBecause the tree is a binary tree, if left and right subtrees of a vertex are of different shapes, after swapping them, the new tree is considered different from the original one.
有一个造二叉树的程序,初始时只有一个节点并且光标指向它,然后进行了n次操作,每次操作属于以下5种操作之一:
- 光标指向当前节点的父亲节点.
- 光标指向当前节点的左儿子.
- 光标指向当前节点的右儿子.
- 随机造一棵包含x个节点的二叉树,把它作为当前节点的左子树.
- 随机造一棵包含x个节点的二叉树,把它作为当前节点的右子树.
给出这n个操作,求在满足所有操作合法的前提下,共有多少种可能的二叉树被造出来,对109+7取模.
1≤n≤500,∑x<500.
【分析】
这是我做的最难的卡特兰数了,然而难在打的方面,真的要想清楚才行啊!!主要是dp部分,卡特兰数是求二叉树形态的。
当只有操作1~3,我们可以准确知道这棵树,就是说我们可以准确知道这棵树的一部分,
然后对于操作4~5,我们知道要插入i棵小树,一共j的点这样的信息。
当要插入一颗大小为k的树时,方案数=卡特兰(k),
然后dp求出用j个节点构成i棵可以为空的子树的方案数f[i][j],然后在树上走一遍然后统计即可。
重点是求当前对于询问能插子树的位置以及未知点的个数。
上图,箭头边x->y表示在x那里有一个插子树操作。(箭头指向位置可以为空,因为那个位置是未知的,插了一颗子树但是没有走到)。
无向边表示没有进行插树操作但是走到了这个点,就是说这是给我们知道的对于未知子树的特别信息,是我们可以确定部分子树形态。
操作每个点记录一个is[x],sum[x],sum[x]表示从x开始,不走箭头边能一共走到的点的个数。
is[x]表示从x开始不走箭头边走到的点的插位个数(就是如果没有左孩子,就有1个插位位置,右孩子也一样)
为什么要这样呢?如上图,1,2都有一个插左子树操作,那么3节点必定不是执行1插子树操作时产生的,2左子树上其他点也是如此,所以不能计入1操作的答案里。
所以如果统计1插左子树操作时,询问一下2的sum[x]和is[x]即可,最后加上dp就好了。
代码如下:
1 #include<cstdio> 2 #include<cstdlib> 3 #include<cstring> 4 #include<iostream> 5 #include<algorithm> 6 #include<queue> 7 #include<cmath> 8 using namespace std; 9 #define LL long long 10 #define Mod 1000000007 11 #define Maxn 510 12 13 LL p[2*Maxn],c[Maxn]; 14 LL f[Maxn][Maxn]; 15 16 LL qpow(LL x,LL b) 17 { 18 LL ans=1; 19 while(b) 20 { 21 if(b&1) ans=(ans*x)%Mod; 22 x=(x*x)%Mod; 23 b>>=1; 24 } 25 return ans; 26 } 27 28 LL get_c(LL n) 29 { 30 LL k=qpow(p[n+1],Mod-2),ans=1; 31 ans=(p[2*n]*k)%Mod; 32 k=qpow(p[n],Mod-2); 33 ans=(ans*k)%Mod; 34 return ans; 35 } 36 37 void init() 38 { 39 p[0]=1; 40 for(LL i=1;i<=2*Maxn-10;i++) p[i]=(p[i-1]*i)%Mod; 41 for(LL i=0;i<=Maxn-10;i++) c[i]=get_c(i); 42 memset(f,0,sizeof(f)); 43 f[0][0]=1; 44 for(LL i=0;i<=Maxn-10;i++) f[1][i]=c[i]; 45 46 for(LL i=2;i<=Maxn-10;i++) 47 for(LL j=0;j<=Maxn-10;j++) 48 { 49 // f[i][j]=0; 50 for(LL k=0;k<=j;k++) 51 f[i][j]=(f[i][j]+f[i-1][j-k]*c[k])%Mod; 52 } 53 54 } 55 56 struct node 57 { 58 int x,lc,rc,f; 59 int sum,is; 60 bool al,ar; 61 }t[Maxn];int cnt; 62 63 struct hp 64 { 65 int x,id; 66 bool q; 67 }qy[Maxn];int ql; 68 69 void upd(int x) 70 { 71 t[x].is=0; t[x].sum=1; 72 t[x].sum+=t[x].al?0:t[t[x].lc].sum; 73 t[x].sum+=t[x].ar?0:t[t[x].rc].sum; 74 75 int y; 76 y=t[x].lc?t[t[x].lc].is:1; 77 if(t[x].al) y=0; 78 t[x].is+=y; 79 80 y=t[x].rc?t[t[x].rc].is:1; 81 if(t[x].ar) y=0; 82 t[x].is+=y; 83 } 84 85 int n; 86 bool ffind() 87 { 88 int now=1;cnt=1;ql=0; 89 t[1].lc=t[1].rc=0; 90 t[1].al=t[1].ar=0; 91 upd(1); 92 bool ok=1; 93 for(int i=1;i<=n;i++) 94 { 95 int opt; 96 scanf("%d",&opt);opt++; 97 if(opt==1) 98 { 99 if(now==1) ok=0;100 now=t[now].f;101 }102 else if(opt==2)103 {104 if(!t[now].lc)105 {106 t[++cnt].f=now;107 t[cnt].lc=t[cnt].rc=0;108 t[cnt].al=t[cnt].ar=0;109 upd(cnt);110 t[now].lc=cnt;111 upd(t[cnt].f);112 }113 now=t[now].lc;114 }115 else if(opt==3)116 {117 if(!t[now].rc)118 {119 t[++cnt].f=now;120 t[cnt].lc=t[cnt].rc=0;121 t[cnt].al=t[cnt].ar=0;122 t[now].rc=cnt;123 }124 now=t[now].rc;125 }126 else if(opt==4)127 {128 if(t[now].al||t[now].lc) ok=0;129 int x;130 scanf("%d",&x);131 qy[++ql].id=now;qy[ql].x=x;qy[ql].q=0;132 t[now].al=1;133 }134 else135 {136 if(t[now].ar||t[now].rc) ok=0;137 int x;138 scanf("%d",&x);139 qy[++ql].id=now;qy[ql].x=x;qy[ql].q=1;140 t[now].ar=1;141 }142 }143 return ok;144 }145 146 void dfs(int x)147 {148 if(t[x].lc) dfs(t[x].lc);149 if(t[x].rc) dfs(t[x].rc);150 upd(x);151 }152 153 LL get_ans()154 {155 LL ans=1;156 for(int i=1;i<=ql;i++)157 {158 int now,y=qy[i].x;159 if(qy[i].q==0)160 {161 if(t[qy[i].id].lc) now=t[t[qy[i].id].lc].is,y-=t[t[qy[i].id].lc].sum;162 else now=1;163 }164 else165 {166 if(t[qy[i].id].rc) now=t[t[qy[i].id].rc].is,y-=t[t[qy[i].id].rc].sum;167 else now=1;168 }169 if(y<0) return 0;170 ans=(ans*f[now][y])%Mod;171 }172 return ans;173 }174 175 int main()176 {177 int T,kase=0;178 init();179 while(scanf("%d",&n)!=EOF)180 {181 printf("Case #%d: ",++kase);182 if(!ffind()) {printf("0\n");continue;}183 dfs(1);184 printf("%lld\n",get_ans());185 }186 return 0;187 }
2016-09-21 22:12:01
【HDU 5370】 Tree Maker(卡特兰数+dp)