Livearchive 5741 Wealthy Family

問題

根付き木が与えられる。
それぞれの頂点には重みがついている。


今、この木からちょうどk個の頂点{a1, a2, ..., ak}を選ぶ。
ここで、i != jなる任意の1≦i, j≦kについて、aiがajの先祖であってはならない。


このとき、選んだ頂点の重みの和の最大値を求めよ。

制約条件

木の頂点数≦150000
重みは1以上1000以下の整数

方針

動的計画法による。


dp[i][j]を、i番目の頂点以下で、j個の頂点を選んだときの重みの和の最大値、
とするような動的計画法だと、O(nk^2)かかってTLEで、上手くやる必要がある。


以下のようにする。


木の根から再帰する。
rec(int cur, int *dp)で、dp[i]を、今まで見た頂点のうち、
curと干渉しない頂点だけをi個使って作れる重みの和の最大値が渡されているとする。


recの中で、dpを書き換えて、
自分以下の頂点も使ったときの、i個使ったときに作れる重みの和の最大値とする。


書き換えは、まずdp2[i]を、自分の子以下も使って、作れる重みの最大の和の配列とする。
dp2はdp[]をコピーして初期化し、recに全ての子について、dp2を渡してやればよい。


その後、dp[i]をmax(dp2[i], dp[i - 1] + w[cur])として書き換える。

ソースコード

int n, m, w[150000];
int dp_[150000][301];
vector<vi> e;

void rec(int c, int *dp){
	int *dp2 = dp_[c];
	rep(i, m + 1) dp2[i] = dp[i];
	
	rep(i, e[c].size()) rec(e[c][i], dp2);
	for(int i = m; i >= 0; i--){
		dp[i] = dp2[i];
		if(i > 0 && dp[i - 1] >= 0) dp[i] = max(dp[i], dp[i - 1] + w[c]);
	}
}

int main()
{
	while(cin >> n >> m){
		e.clear(); e.resize(n);
		
		int root;
		
		rep(i, n){
			int p;
			cin >> p >> w[i];
			if(--p < 0) root = i;
			else e[p].pb(i);
		}
		
		int ans[301];
		rep(i, m + 1) ans[i] = -1;
		ans[0] = 0;
		
		rec(root, ans);
		
		if(ans[m] < 0) cout << "impossible" << endl;
		else cout << ans[m] << endl;
	}
	return 0;
}