TopCoder SRM 559 Div1 Medium HatRack

問題

木が与えられる。
この木と同じ頂点数で、葉以外の頂点の子は必ず一つまたは二つで、
なるべく平衡しているような木で、余った葉はなるべく左側から埋められている木を考える。


この木に、与えられた木を対応させる方法は何通りあるか、求めよ。

制約条件

枝の本数≦50

方針

根にする頂点を一つ決める。
そこから、頂点を当てはめて探索する。
子の数が違ったりしたらその時点で枝刈り。

適当にメモ化すると最悪が5msくらいで通る。

ソースコード

vector<vi> e;
ll dp[51][51][52], cld[51];

void dfs(int c, int p){
	cld[c]++;
	rep(i, e[c].size()) if(e[c][i] != p){
		dfs(e[c][i], c);
		cld[c] += cld[e[c][i]];
	}
}
ll rec(int c, int p, int num){
	if(num < 0) return 0;
	if(num != cld[c]) return 0;
	
	if(dp[c][p][num] >= 0) return dp[c][p][num];
	ll &res = dp[c][p][num];
	res = 0;
	
	int cc = 0, cs[51];
	rep(i, e[c].size()) if(e[c][i] != p) cs[cc++] = e[c][i];
	
	if(num == 1) return res = 1;
	if(cc == 1){
		if(num == 2) return res = 1;
		return res = 0;
	}
	if(cc > 2) return res = 0;
	
	int a = 1;
	while(1){
		int b = a * 2 + 1;
		if(b > num) break;
		a = b;
	}
	int l = min(num - a, a / 2 + 1) + a / 2;
	
	rep(i, cc){
		res += rec(cs[i], c, l) * rec(cs[1 - i], c, num - l - 1);
	}
	return res;
}

class HatRack {
	public:
	long long countWays(vector <int> knob1, vector <int> knob2) 
	{
		int n = knob1.size() + 1;
		e.clear(); e.resize(n);
		
		rep(i, n - 1){
			int a = knob1[i] - 1;
			int b = knob2[i] - 1;
			e[a].pb(b); e[b].pb(a);
		}
		ll ans = 0;
		rep(i, n){
			memset(cld, 0, sizeof(cld));
			memset(dp, -1, sizeof(dp));
			dfs(i, i);
			ans += rec(i, i, n);
		}
		return ans;
	}
};