把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

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);
	}
}
posted @ 2021-08-09 21:55  275307894a  阅读(51)  评论(0编辑  收藏  举报
浏览器标题切换
浏览器标题切换end