Typical DP Contest P - うなぎ

問題

N頂点からなる木に、交わらないK本のパスを書く書きかたは何通りあるか。mod 10^9 + 7で求めよ。
パスを書く順序は区別しない。

制約条件

N≦1000
K≦50

解法

dp[i][j][k] :=
頂点iを根とする部分木で、j本のパスを描き、頂点iから出るパスの本数をk本とするときの場合の数
を更新するようなDPをすればよい。


更新するのにはもう一回小さいDPをすればよくて、
dp2[子の何番目まで見たか][今までの子まででのパスの本数][親にいるパスの本数] := 場合の数
として、子ごとに更新していけばよい。


更新は、親にパスをつなぐかどうか2通りの遷移がある。

ソースコード

const int mod = inf + 7;
int n, path;
int dp[1000][52][3];
vector<vi> e;

void rec(int c, int p){
	
	vector<vector<vi> > dp2(2, vector<vi>(52, vi(3)));
	int cur = 0, next = 1;
	
	dp2[0][0][0] = 1;
	
	rep(i, e[c].size()) if(e[c][i] != p){
		
		rec(e[c][i], c);
		for(int j = path; j >= 0; j--) rep(k, 3){
			
			rep(l, path - j + 2) rep(m, 3){
				
				int way = (ll)dp2[cur][j][k] * dp[e[c][i]][l][m] % mod;
				//つなげる
				if(m < 2 && k < 2){
					int nj = j + l;
					if(k == 0 && m == 0) nj++;
					if(k == 1 && m == 1) nj--;
					if(0 <= nj && nj <= path) (dp2[next][nj][k + 1] += way) %= mod;
				}
				//つなげない
				if(j + l <= path) (dp2[next][j + l][k] += way) %= mod;
			}
		}
		swap(cur, next);
		dp2[next] = vector<vi>(52, vi(3));
	}
	rep(i, path + 1) rep(j, 3) dp[c][i][j] = dp2[cur][i][j];
}

int main(){
	cin >> n >> path;
	e.resize(n);
	rep(i, n - 1){
		int a, b;
		cin >> a >> b; a--; b--;
		e[a].pb(b);
		e[b].pb(a);
	}
	rec(0, 0);
	int ans = 0;
	rep(i, 3) (ans += dp[0][path][i]) %= mod;
	cout << ans << endl;
	
	return 0;
}