BZOJ 3057圣主的考验题解

老师居然考这么毒瘤的题目!!!!!

很容易想到dp,f[i][j]表示有i个节点,左子树的最深深度为j的方案数

枚举左子树有多少节点然后转移,复杂度为n^3

T飞~

我们考虑到有深度为h的树的节点有多少,可以发现深度为h的节点有着一定的范围

设minn为深度为h的树最少有多少节点,maxh为深度为h的数最多有多少节点

很显然minn[h]=minn[h-1]+minn[h-2]+1,maxh[h]=2^h-1

我们每一次转移时依然枚举左子树节点个数i,再次枚举深度是只需要枚举所有minn[h]<=i,切maxh[h]>=h的深度h

这样复杂度就变成了n^2*h,h为maxh[h]>3000的第一个h大约为20

但是它神奇的T掉了,我也不知道为什么......

# include<cstdio>
# include<cstring>
# include<cstdlib>
# include<algorithm>
using namespace std;
typedef long long LL;
const int mn = 3000;
const int mod = 1000000000;
int maxh[31],minh[31],L[mn + 10],R[mn + 10];
LL dp[mn + 10][31],ans[mn + 10];
 
int main ()
{
    dp[0][0]=dp[1][1]=1, ans[1]=1;
    minh[1]=1,minh[2]=2;
    for(int i=3;i<=30;i++)
        minh[i]=minh[i-1]+minh[i-2]+1;
    for(int i=1;minh[i]<=mn && i<= 30;i++)
        for(int j=minh[i];j<min(minh[i+1],mn+1);j++)
            R[j]=i;
    maxh[1]=1;
    for(int i=2;i<=30;i++)
      maxh[i]=maxh[i-1]*2+1;
     L[1]=1;
     for(int i=2;maxh[i-1]<=mn && i<=30;i++)
        for(int j=maxh[i-1]+1;j<=min(maxh[i],mn);j++)
            L[j]=i;
     for(int i=2;i<=mn;i++)
     {
        for(int l=0;l<i;l++)
        {
            int r=i-1-l;
            for (int h=max(0,L[i]-2);h<=min(29,R[i]-1);h++)
            {
                if(h)
                dp[i][h+1]+=dp[l][h]*dp[r][h-1];
                (dp[i][h+1]+=dp[l][h]*dp[r][h])%=mod;
                dp[i][h+2]+=dp[l][h]*dp[r][h+1];
            }
        }
        for(int h=1;h<=min(29,i);h++)
            ans[i]+=dp[i][h];
        ans[i] %= mod;
    }
    int n;
    while(scanf("%d",&n),n)
       printf(n>=38 ? "%09lld\n" : "%lld\n",ans[n]);
    return 0;
}

 

posted @ 2018-09-18 21:41  logeadd  阅读(314)  评论(0编辑  收藏  举报