【HDU 5370】 Tree Maker(卡特兰数+dp)

Tree Maker

Problem Description

Tree 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?

Input

The 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.

Output

For 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 Input

2

3 3

4 3

2

3 3

1

Sample Output

Case #1: 25

Case #2: 5

Hint

Because 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种操作之一:

  1. 光标指向当前节点的父亲节点.
  2. 光标指向当前节点的左儿子.
  3. 光标指向当前节点的右儿子.
  4. 随机造一棵包含x个节点的二叉树,把它作为当前节点的左子树.
  5. 随机造一棵包含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         else
135         {
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         else
165         {
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 }

[HDU 5370]

2016-09-21 22:12:01

时间: 2025-01-12 20:47:44

【HDU 5370】 Tree Maker(卡特兰数+dp)的相关文章

hdu 5370 Tree Maker(catalan+dp)

题目链接:hdu 5370 Tree Maker n个节点的二叉树种类为Catalan数的第n项 对于一棵子树而言,被移动过的节点就是确定的位置,所以只要知道已经确定位置的K个节点有多少个空孩子指针M,和就该子树下的N个未确定位置的节点,等于是说用N个节点构造M个可为空的子树的种类数.对于整个树的形态数即为若干棵独立的子树形态数的乘积. 定义dp[i][j]为用i个节点构造j棵树的形态数,dp[i][j] = sum{ dp[i-1][j-k] * catalan[k] | 0 ≤ k ≤j }

HDU 5370 Tree Maker

一个显然的结论是,一棵nn个结点的二叉树的形态数,是Catalan数第nn项.

LA 5092 &amp;&amp; hdu 3723 Delta Wave (卡特兰数)

题目:http://acm.hdu.edu.cn/showproblem.php?pid=3723 and http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=20568 题意:有种折线每向右延伸一个单位长度,高度要么不变,要么加1,要么减1.而且任何时刻高度不能低于0.求这种折线最终高度为0的情况总数. 分析:由于任何时刻斜向上的线不小于斜向下的线的数目,而且最终相等,,,,,卡特兰数模型.卡特兰数资料 若有i条斜向上的线,那

HDU 5673 Robot(卡特兰数)

题目链接:点击打开链接 思路:卡特兰数可以用来求括号序列的个数, 用了组合数学的知识. 该题其实就等价于求一个括号序列的个数, 因为满足任意时刻, 向右的步数大于等于向左的步数. 但是该题还有停止不动的情况, 所以我们不妨枚举向右的步数, 然后求出括号序列的组合数, 然后剩下的就是停止不动的步数, 用组合数插空即可. 另外, 除法取模要取逆元, 我们可以线性预处理出所有逆元. 细节参见代码: #include<cstdio> #include<cstring> #include&l

hdu5816 卡特兰数+dp

题意:共n张无中生有,m张攻击牌.每张攻击牌攻击力已知,敌方有p点血.随机洗牌.游戏开始,己方抽取一张手牌,若是无中生有则可再抽两张牌.求能在第一回合内将敌方杀死的概率. n+m <= 20, p <= 1000; 很明显,与卡特兰数有关,原先栈内数量为1,抽到无中生有即入栈,否则出栈. 枚举攻击牌,求出该攻击牌组合下,用完所有手牌将对方杀死的方案数,以及抽光所有牌将对方杀死的方案数(手牌有剩). 不算预处理的复杂度,每组数据的时间复杂度为O(2^m) 1 #include <cstdi

HDU 4828 Grids(卡特兰数+乘法逆元)

首先我按着我的理解说一下它为什么是卡特兰数,首先卡特兰数有一个很典型的应用就是求1~N个自然数出栈情况的种类数.而这里正好就对应了这种情况.我们要满足题目中给的条件,数字应该是从小到大放置的,1肯定在左上角,所以1入栈,这时候我们放2,如果我们把2放在了1的下面就代表了1出栈,把2放在上面就代表了2也进栈(可以看一下hint中第二组样例提示),以此类推,这样去放数,正好就对应了上面一行入栈,下面一行出栈的情况,一共n行,对应上限为n的卡特兰数. 需要注意的地方就是在使用卡特兰数递推式的时候,除法

hdu 1130How Many Trees?(卡特兰数)

卡特兰数又称卡塔兰数,英文名Catalan number,是组合数学中一个常出现在各种计数问题中出现的数列. 以比利时的数学家欧仁·查理·卡塔兰 (1814–1894)的名字来命名,其前几项为(从第零项开始) : 1, 1, 2, 5, 14, 42, 132, 429, 1430, 4862, 16796, 58786, 208012, 742900, 2674440, 9694845, 35357670, 129644790, 477638700, 1767263190, 656412042

HDU 5293 Tree chain problem 树形dp+dfs序+树状数组+LCA

题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=5293 题意: 给你一些链,每条链都有自己的价值,求不相交不重合的链能够组成的最大价值. 题解: 树形dp, 对于每条链u,v,w,我们只在lca(u,v)的顶点上处理它 让dp[i]表示以i为根的指数的最大值,sum[i]表示dp[vi]的和(vi为i的儿子们) 则i点有两种决策,一种是不选以i为lca的链,则dp[i]=sum[i]. 另一种是选一条以i为lca的链,那么有转移方程:dp[i]=

HDU 6228 tree 简单思维树dp

一.前言 前两天沈阳重现,经过队友提点,得到3题的成绩,但是看到这题下意识觉得题目错了,最后发现实际上是题目读错了....GG 感觉自己前所未有的愚蠢了....不过题目读对了也是一道思维题,但是很好理解. 二.题意 对于一个无相无环图,要求找出若干边,满足"这些边被至少K个不同的点集在互相联通的时候访问到".或者说"这些边都包含在K个不同的点集个字组成的联通快里面". 三.题解 考虑如何表示一个边,以及这条边两边的点的数量?(这是一棵树)作为一颗树,就有树边概念,因