题目描述

既然是萌萌哒$visit\text{_}world$的比赛,那必然会有一道计数题啦!
考虑一个$N$个节点的二叉树,它的节点被标上了$1\sim N$的编号。并且,编号为$i$的节点在二叉树的前序遍历中恰好是第$i$个出现。
我们定义$A_i$表示编号为$i$的点在二叉树的中序遍历中出现的位置。
现在,给出$M$个限制条件,第$i$个限制条件给出了$u_i,v_i$,表示$A_{u_i},A_{v_i}$你需要计算有多少种不同的带标号二叉树满足上述全部限制条件,答案对$10^9+7$取模。


输入格式

第一行一个整数$T(1\leqslant T\leqslant 5)$,表示数据组数。
每组数据第一行为两个整数$N,M$,意义如题目所述。
接下来$M$行,每行两个整数$u_i,v_i(1\leqslant u_i,v_i\leqslant N)$。描述一条限制。


输出格式

对每组数据,输出一行一个整数,表示答案。


样例

样例输入:

3
5 0
3 2
1 2
2 3
3 3
1 2
2 3
3 1

样例输出:

42
1
0


数据范围与提示

样例解释:

第一组数据,无任何限制时,$5$个点的二叉树形态个数为$42$。
第二组数据,唯一满足条件的二叉树的形态为$1-2-3$。($1$为根,$2,3$均为右儿子)。
第三组数据,限制条件产生矛盾。

数据范围:

对于全部的测试数据,保证$T\leqslant 5,N\leqslant 400,M\leqslant 10^3$
子任务$1$($20$分):$M=0$。
子任务$2$($15$分):$N\leqslant 10$。
子任务$3$($20$分):$N\leqslant 50,M\leqslant 1$。
子任务$4$($15$分):$N\leqslant 50$。
子任务$5$($30$分):无特殊限制。


题解

先来复习一下三种遍历序:

  $\alpha.$前(先)序遍历:根$\rightarrow$左$\rightarrow$右。

  $\beta,$中序遍历:左$\rightarrow$中$\rightarrow$右。

  $\gamma.$后序遍历:左$\rightarrow$右$\rightarrow$根。

为方便记忆,我们可以只记“根”在其中的位置就好了,先序遍历根在前,中序遍历根在中,后序遍历根在后,左在前右在后。

不妨从$M=0$的情况入手,你可能找规律发现是卡特兰数,简单证明一下:

  可以把放到左儿子认为是$+1$,放到右儿子认为是$-1$,所以是卡特兰数。

但是如果接着往下思考它就死了……

考虑换一个思路解决这个问题,考虑$DP$,设$dp[l][r]$表示点的编号区间$[l,r]$所能组成的不同形态的数的个数。

因为需要满足前序遍历序就是点的编号,所以$l$一定是根节点,且左右子树里点的编号也是分别连续的,所以我们可以得到转移方程:

$$dp[l][r]=\sum \limits_{i=l}^r dp[l+1][i]\times dp[i+1][r]$$

接着考虑带限制的情况,其实也很简单;不妨设两点$u$和$v$,且$u$的编号比$v$小。

如果存在限制要求在中序遍历中$v$在$u$之前,那么$v$一定要在$u$的左子树中;反之同理。

于是我们可以预处理出来一定在左子树中的编号最大的点(设为$L[i]$)和一定在右子树中的编号最小的点(设为$R[i]$),那么我们可以得到:

$$dp[l][r]=\sum \limits_{i=L[l]}^{R[l]}dp[l+1][i]\times dp[i+1][r])$$

于是这道题就解决了。

时间复杂度:$\Theta(n^3)$。

期望得分:$100$分。

实际得分:$100$分。


代码时刻

#include<bits/stdc++.h>
using namespace std;
const int mod=1000000007;
int N,M;
bool Map[401][401];
int L[401],R[401];
long long dp[401][401];
void pre_work()
{
memset(Map,0,sizeof(Map));
memset(dp,-1,sizeof(dp));
}
long long dfs(int l,int r)
{
if(l>r)return 1;
if(L[l]>r)return 0;
if(l==r)return 1;
if(dp[l][r]!=-1)return dp[l][r];
dp[l][r]=0;
for(int i=L[l];i<=min(R[l],r);i++)
dp[l][r]=(dp[l][r]+dfs(l+1,i)*dfs(i+1,r)%mod)%mod;
return dp[l][r];
}
int main()
{
int T;scanf("%d",&T);
while(T--)
{
pre_work();
scanf("%d%d",&N,&M);
for(int i=1;i<=M;i++)
{
int u,v;scanf("%d%d",&u,&v);
Map[u][v]=1;
}
for(int i=1;i<=N;i++)
{
L[i]=R[i]=i;
for(int j=i+1;j<=N;j++)if(Map[j][i])L[i]=j;
for(int j=i+1;j<=N;j++){if(Map[i][j])break;R[i]=j;}
}
printf("%lld\n",max(0LL,dfs(1,N)));
}
return 0;
}

rp++

[CSP-S模拟测试]:计数(DP+记忆化搜索)的更多相关文章

  1. 【bzoj5123】[Lydsy12月赛]线段树的匹配 树形dp+记忆化搜索

    题目描述 求一棵 $[1,n]$ 的线段树的最大匹配数目与方案数. $n\le 10^{18}$ 题解 树形dp+记忆化搜索 设 $f[l][r]$ 表示根节点为 $[l,r]$ 的线段树,匹配选择根 ...

  2. 【BZOJ】1415 [Noi2005]聪聪和可可 期望DP+记忆化搜索

    [题意]给定无向图,聪聪和可可各自位于一点,可可每单位时间随机向周围走一步或停留,聪聪每单位时间追两步(先走),问追到可可的期望时间.n<=1000. [算法]期望DP+记忆化搜索 [题解]首先 ...

  3. [题解](树形dp/记忆化搜索)luogu_P1040_加分二叉树

    树形dp/记忆化搜索 首先可以看出树形dp,因为第一个问题并不需要知道子树的样子, 然而第二个输出前序遍历,必须知道每个子树的根节点,需要在树形dp过程中记录,递归输出 那么如何求最大加分树——根据中 ...

  4. poj1664 dp记忆化搜索

    http://poj.org/problem?id=1664 Description 把M个相同的苹果放在N个相同的盘子里,同意有的盘子空着不放,问共同拥有多少种不同的分法?(用K表示)5.1.1和1 ...

  5. 状压DP+记忆化搜索 UVA 1252 Twenty Questions

    题目传送门 /* 题意:给出一系列的01字符串,问最少要问几个问题(列)能把它们区分出来 状态DP+记忆化搜索:dp[s1][s2]表示问题集合为s1.答案对错集合为s2时,还要问几次才能区分出来 若 ...

  6. ACM International Collegiate Programming Contest, Tishreen Collegiate Programming Contest (2017)- K. Poor Ramzi -dp+记忆化搜索

    ACM International Collegiate Programming Contest, Tishreen Collegiate Programming Contest (2017)- K. ...

  7. POJ 1088 DP=记忆化搜索

    话说DP=记忆化搜索这句话真不是虚的. 面对这道题目,题意很简单,但是DP的时候,方向分为四个,这个时候用递推就好难写了,你很难得到当前状态的前一个真实状态,这个时候记忆化搜索就派上用场啦! 通过对四 ...

  8. bzoj1833: [ZJOI2010]count 数字计数(数位DP+记忆化搜索)

    1833: [ZJOI2010]count 数字计数 题目:传送门 题解: 今天是躲不开各种恶心DP了??? %爆靖大佬啊!!! 据说是数位DP裸题...emmm学吧学吧 感觉记忆化搜索特别强: 定义 ...

  9. zoj 3644(dp + 记忆化搜索)

    题目链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=4834 思路:dp[i][j]表示当前节点在i,分数为j的路径条数,从 ...

  10. loj 1044(dp+记忆化搜索)

    题目链接:http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=26764 思路:dp[pos]表示0-pos这段字符串最少分割的回文 ...

随机推荐

  1. 递归法求组合数C(m,n)

    假设这样一个数组: 1 2 3 4 5 n=5 若 m=3 也就是要求C(3,5) 首先先选第一个数 1 那么剩下的工作就是在2-5之间选择2个数 如果我们没有选择第一个数 选第二个数2 那么剩下的工 ...

  2. [DS+Algo] 002 一维表结构

    目录 1. 顺序表 1.1 分类 1.2 实现方式 1.3 扩容问题 1.4 操作 2. 链表 2.1 分类 2.2 链表相关操作 2.3 链表 VS 顺序表 3. 关于代码实现 1. 顺序表 1.1 ...

  3. Linux命令(持续更新)

    1. tail 命令    tail 命令可用于查看文件的内容,有一个常用的参数 -f 常用于查阅正在改变的日志文件. tail  -f  filename 会把 filename 文件里的最尾部的内 ...

  4. python中session的使用

  5. 【NOI2007】项链工厂 ——老题新做.jpg

    第一次是用 ODT 过的...(虽说跑得飞慢但它就是能过) 而且还写了发题解... 第二次是在考场上碰到了这道题,然后居然打了线段树,各种 bug 直接让代码爆零 但还是补好了代码重新交了一发,发现跑 ...

  6. ISC2016训练赛 phrackCTF--Smali

    测试文件:https://static2.ichunqiu.com/icq/resources/fileupload/phrackCTF/REVERSE/Crackme.smali 参考资料:http ...

  7. wordpress添加index页面跳转链接

    1. 制作page页面 1.1 在themes下的主题目录新建一个page页面 1.2 在wordpress后台新建页面跟在目录页面中相同名字的页面文件 1.3 复制后台页面中的古定链接 1.4 在i ...

  8. Timer的利用

    package 第十一章; import java.util.*; import java.util.TimerTask; public class TimerTest { /** * @param ...

  9. Pure播放器

    我是的本地应用,并不会拿你的隐私数据.

  10. Apache Allow 命令

    用于设置哪些客户端可以访问 Apache,格式如下: Allow from [All /全域名/部分域名/IP 地址/网络地址/CIDR 地址] All:表示全部客户端 全域名:表示域名对应的客户端, ...