HDU 4359 Easy tree DP? -- 我只能說是個dp
阿新 • • 發佈:2019-02-14
/* HDU 4359 Easy tree DP? 題意:求任意節點下左子樹最大值小於右子樹最大值的二叉樹方案數 我只能說這是一個dp(rt) 狀態:i個節點在滿足不大於j深度有 dp[i][j] 種方案數 初始化: CLR(dp,0) dp[1][1~360] = 1 各種深度僅一個節點方案數為1 轉移: (1)i箇中挑一個為root 並且該root僅有左子樹(or右子樹,so ×2) dp[i][j] += dp[i-1][j-1]*nCr(i,i-1)*2 (2)i箇中挑一個為root 並且該root有左子樹(假設擁有k個節點)and右子樹 dp[i][j] += nCr(i-2,k) * 從i-2(因為去掉了一個root和一個必須放在右子樹的最大節點)箇中挑k個的方案數 dp[k][j-1] * 從i-2箇中挑出的k個作為root的左子樹的方案數 (因為沒算root那一層所以j-1) dp[i-1-k][j-1] * 上面挑剩下的i-1-k個節點構成{深度<=j-1}的方案數 (因為沒算root那一層所以j-1) nCr(i,i-1) 從i箇中挑1個為root的方案數 */ #pragma comment(linker, "/STACK:102400000,102400000") #include <functional> #include <algorithm> #include <iostream> #include <cstring> #include <cstdio> #include <string> #include <vector> #include <ctime> #include <queue> #include <cmath> #include <set> #define CLR(a,v) memset(a,v,sizeof(a)) using namespace std; typedef long long ll; typedef pair<int,int> pii; const int N = 360 + 10; const ll MOD = 1e9 + 7; int n,d; ll C[N][N]={0}; ll dp[N][N]={0}; // i個節點在滿足不大於j深度有 dp[i][j] 種方案數 ll nCr(ll n,ll r){ if(C[n][r]!=-1)return C[n][r]; if(n==r)return 1; if(n<r)return 0; if(r==1)return n; return C[n][r] = (nCr(n-1,r-1) + nCr(n-1,r))%MOD; } void pre(){ CLR(C,-1); int n_p = 360; int depth = 360; // 初始化各種深度,0個點方案數為1,1個點方案數也為1 for(int j = 1 ; j <= depth ; j++) dp[1][j] = 1; for(int i = 2 ; i <= n_p ; i++){ for(int j = 1 ; j <= depth ; j++){ dp[i][j] = nCr(i,1)*dp[i-1][j-1]%MOD *2%MOD; if(dp[i][j] >= MOD) dp[i][j] %= MOD; for(int k = 1 ; k <= i-2 ; k++){ // 雖然選取一個做為root,但是剩餘中最大的應該在最右葉子上,因此去掉兩個點 // 保證root左子樹不為空,因此 k >= 1 // k表示 root左子樹上有k個點 // 左k個點 右i-1-k個點 左挑k個 root的挑法 dp[i][j] += dp[k][j-1] * dp[i-1-k][j-1]%MOD * nCr(i-2,k)%MOD * nCr(i,1) % MOD; if(dp[i][j] >= MOD) dp[i][j] %= MOD; } } } } int main(){ //freopen("in.txt","r",stdin); //freopen("Output.txt","w",stdout); pre(); int T,ca=0;cin >> T; while(T--){ scanf("%d%d",&n,&d); printf("Case #%d: %I64d\n",++ca, (dp[n][d] - dp[n][d-1] + MOD)%MOD); } return 0; }