hdu6854 Kcats 2020hdu多校7

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;
}

猜你喜欢

转载自blog.csdn.net/liufengwei1/article/details/109089329