2018.10.16.2

給定 N,MN, M,對於任意的 1kN1 \leq k \leq N,求出葉子數爲 kk,滿足每個葉子到根的路徑上左向邊數量少於 MM,每個節點都有 0022 個子節點,區分左右的二叉樹數量

N,M5000N, M \leq 5000


一般的DP是 O(n3)O(n^3) 的,所以我們要優化一下狀態

我們考慮像 dfs 遍歷整棵樹,設 f(i,j)f(i, j) 表示當前的樹有 ii 個葉子,遍歷到的點左向邊數量爲 jj 的方案數

我們新增一個節點時,葉子數量有可能不變(向左搜索),也有可能增加一個葉子(回溯後向右搜索),葉子數不變時,左向邊數增加了 11,葉子數增加時,左向邊數量減少了 11(因爲每個非葉子節點都有 22 個子節點),所以我們有
f(i,j)=f(i,j1)+f(i1,j+1) f(i, j) = f(i, j-1) +f(i-1, j+1)

那麼最後 f(i,0)f(i, 0) 就是葉子數爲 ii 時的答案,時間複雜度 O(n2)O(n^2)

#include <bits/stdc++.h>
using namespace std;

typedef long long ll; 

const int N = 6000; 
const int MOD = 998244353;

int n, m; ll f[N][N];

inline ll add( ll a, ll b ) { return a + b >= MOD ? a + b - MOD : a + b; }

int main()
{
	scanf( "%d %d", &m, &n ); 

	f[1][0] = 1; 
	
	for( int i = 1; i < n; i ++ )	
		for( int j = 0; j < m; j ++ )
		{
			if( j < m - 1 ) f[i][j+1] = add( f[i][j+1], f[i][j] ); 
			if( j > 0 ) f[i+1][j-1] = add( f[i+1][j-1], f[i][j] ); 
 		}
	
	for( int i = 1; i <= n; i ++ )
		printf( "%lld\n", f[i][0] ); 
	
	return 0; 
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章