http://acm.hdu.edu.cn/showproblem.php?pid=6854
好DP
学习自https://www.cnblogs.com/chasedeath/p/13485478.html
很显然最右边的1就是pi=1所在的位置,然后i的右边全都多1,左边不多1,那么左右变成了两个完全分开的部分,由于剩下的数字分哪些去左右都一样,所以乘以一个组合数
接下来就是神奇的区间DP。。。dp[i][j][k]表示从i到j,根节点左子树的祖先有k个的方案数,也就是当前区间的根节点在全局的笛卡尔树深度为k的方案数,然后我们枚举根节点,如果根节点id,a[id]==-1,那么左边的根节点祖先数量1-n都行,否则就只能恰好为a[id]个。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxl=110;
const int mod=1e9+7;
int n;
int a[maxl];ll fac[maxl],inv[maxl];
ll c[maxl][maxl];
ll dp[maxl][maxl][maxl];
inline ll qp(ll a,ll b)
{
ll ans=1,cnt=a;
while(b)
{
if(b&1)
ans=ans*cnt%mod;
cnt=cnt*cnt%mod;
b>>=1;
}
return ans;
}
inline ll C(int n,int r)
{
if(r>n || r<0) return 0;
return fac[n]*inv[n-r]%mod*inv[r]%mod;
}
inline void prework()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
}
inline void add(ll &x,ll y)
{
x+=y;
if(x>=mod)
x-=mod;
}
inline void mainwork()
{
int l,r;
memset(dp,0,sizeof(dp));
for(int i=n;i>=1;i--)
for(int j=i;j<=n;j++)
for(int k=i;k<=j;k++)
{
if(a[k]==-1) l=1,r=n;
else l=r=a[k];
for(int d=l;d<=r;d++)
add(dp[i][j][d],(i<k?dp[i][k-1][d]:1)*(k<j?dp[k+1][j][d+1]:1)%mod*c[j-i][k-i]%mod);
}
}
inline void print()
{
printf("%lld\n",dp[1][n][1]);
}
int main()
{
fac[0]=1;
for(int i=1;i<=100;i++)
fac[i]=fac[i-1]*i%mod;
inv[100]=qp(fac[100],mod-2);
for(int i=99;i>=0;i--)
inv[i]=inv[i+1]*(i+1)%mod;
for(int i=0;i<=100;i++)
for(int j=0;j<=100;j++)
c[i][j]=C(i,j);
int t;
scanf("%d",&t);
for(int i=1;i<=t;i++)
{
prework();
mainwork();
print();
}
return 0;
}