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