BZOJ #3057. 圣主的考验
题面传送门
首先设\(dp_{i,j}\)为\(i\)个点的树高度为\(j\)的方案数,这个很好\(O(n^3)\)做。
然后发现因为树很平衡,所以树高大概是\(O(logn)\)级别的,然后就\(O(n^2logn)\)可以过了。
但是还有更优的方法。
我们设\(Up_i\)为高度为\(i\)的树的最小点数,\(Down_i\)表示高度为\(i\)的树的最大点数,所以这个随便dp一下。
然后我们发现每个点最多只会在很少个区间内。
然后直接dp就好了,复杂度大概是\(O(n^2)\)的但是跑得比\(O(n^2)\)还快。
code:
#include<bits/stdc++.h>
#define I inline
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define abs(x) ((x)>0?(x):-(x))
#define re register
#define ll long long
#define db double
#define N 3000
#define K 150
#define mod 1000000000
#define eps (1e-9)
#define U unsigned int
#define it iterator
#define Gc() getchar()
#define Me(x,y) memset(x,y,sizeof(x))
#define d(x,y) (n*(x-1)+(y))
using namespace std;
int T,x;ll dp[N+5][50],Ans[N+5],Up[N+5],Down[N+5],now,po=1e8;
int main(){
freopen("1.in","r",stdin);
re int i,j,h;Down[1]=1;Up[1]=1;for(i=2;i<=30;i++) Down[i]=Down[i-1]*2+1,Up[i]=Up[i-1]+Up[i-2]+1;Ans[1]=dp[1][1]=dp[0][0]=1;for(i=2;i<=N;i++){
for(j=1;j<=30;j++){
if(i<Up[j]||i>Down[j]) continue;for(h=Up[j-1];h<=min(Down[j-1],i-1);h++) dp[i][j]=(dp[i][j]+dp[h][j-1]*dp[i-h-1][j-1]+dp[h][j-1]*dp[i-h-1][j-2]*2)%mod;Ans[i]+=dp[i][j];
}Ans[i]%=mod;
}scanf("%d",&x);while(x){
now=Ans[x];if(x<=35)printf("%lld\n",now);else {po=1e8;while(po^1&&now<po) putchar('0'),po/=10;printf("%lld\n",now);}scanf("%d",&x);
}
}