NOIP 模拟 $53\; \rm 计数$
题解 \(by\;zj\varphi\)
首先考虑几个性质,对于一棵二叉树,其实它的前序遍历就是它的 \(dfs\) 序,那么:
- 这棵树的每棵子树都是连续的一段。
- 这颗左子树的节点编号都小于右子树
- 这棵树任意一个有左子节点的节点的左子节点编号必定为该节点编号 \(+1\)
那么不妨设 \(a<b\),分两种情况:
- \(A_a<A_b\),那么这种情况就要求 \(b\) 不在 \(a\) 的左子树中,体现到 \(dfs\) 序上就是 \(a\) 的左子树的一段序列不包括 \(b\),即 \(a\) 的左子树大小最大为 \(b-a-1\)
- \(A_a>A_b\),这种情况只可能是 \(b\) 在 \(a\) 的左子树中,体现到 \(dfs\) 序上就是 \(a\) 的左子树的一段序列包括 \(b\),即 \(a\) 的左子树大小最小为 \(b-a\)
这样就可以在最开始的时候把所有节点的左子树大小限制求出来,然后转移:设 \(dp_{i,j}\) 表示以 \(i\) 为根,子树大小为 \(j\) 的合法方案数
\[dp_{i,j}=\sum_{k=min_i}^{max_i}dp_{i+1,k}*dp_{i+k+1,j-k-1}
\]
其中 \(min_i,max_i\) 分别表示 \(i\) 节点左子树的最小和最大极限值。
复杂度 \(\mathcal{O\rm(Tn^3)}\)
Code
#include<bits/stdc++.h>
#define Re register
#define ri Re signed
#define pd(i) ++i
#define bq(i) --i
namespace IO{
char buf[1<<21],*p1=buf,*p2=buf;
#define gc() p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?(-1):*p1++
struct nanfeng_stream{
template<typename T>inline nanfeng_stream &operator>>(T &x) {
Re bool f=false;x=0;Re char ch=gc();
while(!isdigit(ch)) f|=ch=='-',ch=gc();
while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=gc();
return x=f?-x:x,*this;
}
}cin;
}
using IO::cin;
namespace nanfeng{
#define FI FILE *IN
#define FO FILE *OUT
template<typename T>inline T cmax(T x,T y) {return x>y?x:y;}
template<typename T>inline T cmin(T x,T y) {return x>y?y:x;}
using ll=long long;
static const int N=440,MOD=1e9+7;
int lm[2][N],T,n,m;
ll dp[N][N];
inline int main() {
FI=freopen("count.in","r",stdin);
FO=freopen("count.out","w",stdout);
cin >> T;
for (ri z(1);z<=T;pd(z)) {
cin >> n >> m;
memset(lm[1],0x3f,sizeof(int)*(n+1));
memset(lm[0],0,sizeof(int)*(n+1));
for (ri i(1),a,b;i<=m;pd(i)) {
cin >> a >> b;
if (a>b) {
std::swap(a,b);
lm[0][a]=cmax(lm[0][a],b-a);
} else lm[1][a]=cmin(lm[1][a],b-a-1);
}
bool fg=false;
for (ri i(1);i<=n;pd(i)) if (lm[0][i]>lm[1][i]) {fg=true;break;}
if (fg) {printf("0\n");continue;}
memset(dp,0,sizeof(dp));
auto MD=[](ll x) {return x>=MOD?x-MOD:x;};
for (ri i(n);i;bq(i)) {
if (!lm[0][i]) dp[i][1]=1;
for (ri j(lm[0][i]+1);j<=n-i+1;pd(j)) {
int mk=cmin(lm[1][i],j-1);
for (ri k(lm[0][i]);k<=mk;pd(k)) dp[i][j]=MD(dp[i][j]+dp[i+1][k]*dp[i+k+1][j-1-k]%MOD);
if (mk==j-1) dp[i][j]=MD(dp[i][j]+dp[i+1][j-1]);
if (!lm[0][i]) dp[i][j]=MD(dp[i][j]+dp[i+1][j-1]);
}
}
printf("%lld\n",dp[1][n]);
}
return 0;
}
}
int main() {return nanfeng::main();}