题解 计数
- 多测题一定要检查一遍solve函数里有没有写
exit(0)
!
没有限制的情况很简单,DP或记搜都行,考虑枚举左子树大小
有限制的情况:
有一步转化:
令 \(A_i\) 为 \(i\) 在中序遍历里的排名
当 \(u<v\) 时,若让 \(A_u < A_v\),发现 \(v\) 一定在 \(u\) 的右子树里
这就对 \(u\) 的左子树大小产生了限制
若左子树过大,\(u\) 会落到左子树
\(u>v\) 同理
于是记录 \(l[i], r[i]\) 为 \(i\) 的可行左子树大小范围
于是可以正常DP
- 在树的遍历顺序下,两个点的id差,位置和它们的子树大小有时可以相互限制
Code:
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 450
#define ll long long
#define reg register int
//#define int long long
char buf[1<<21], *p1=buf, *p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf, 1, 1<<21, stdin)), p1==p2?EOF:*p1++)
inline int read() {
int ans=0, f=1; char c=getchar();
while (!isdigit(c)) {if (c=='-') f=-f; c=getchar();}
while (isdigit(c)) {ans=(ans<<3)+(ans<<1)+(c^48); c=getchar();}
return ans*f;
}
int n, m;
const ll mod=1e9+7;
namespace task1{
ll rem[N];
ll dfs(int siz) {
--siz;
if (siz<=0) return 1ll;
if (~rem[siz]) return rem[siz];
ll ans=0;
for (int i=0; i<=siz; ++i)
ans=(ans+dfs(i)*dfs(siz-i)%mod)%mod;
return rem[siz]=ans;
}
void solve() {
printf("%lld\n", dfs(n));
}
}
namespace task{
ll rem[N][N];
int l[N], r[N];
ll dfs(int u, int siz) {
if (u>n) return siz==0ll;
if (siz==0) return 1ll;
if (siz<=l[u]) return 0ll;
--siz;
if (siz<=0) return 1ll;
if (~rem[u][siz]) return rem[u][siz];
ll ans=0;
for (int i=l[u]; i<=min(r[u], siz); ++i)
ans=(ans+dfs(u+1, i)*dfs(u+i+1, siz-i)%mod)%mod;
//printf("dp[%d][%d]=%lld\n", u, siz, ans);
return rem[u][siz]=ans;
}
void solve() {
memset(rem, -1, sizeof(rem));
for (int i=1; i<=n; ++i) l[i]=0, r[i]=n-i;
for (int i=1,u,v; i<=m; ++i) {
u=read(); v=read();
if (u<v) r[u]=min(r[u], v-u-1);
else l[v]=max(l[v], u-v);
}
//for (int i=1; i<=n; ++i) cout<<l[i]<<' '<<r[i]<<endl;
printf("%lld\n", dfs(1, n));
}
}
signed main()
{
freopen("count.in", "r", stdin);
freopen("count.out", "w", stdout);
int T;
T=read();
while (T--) {
n=read(); m=read();
task::solve();
}
return 0;
}