Codeforces 414(#240 Div1) D. Mashmokh and Water Tanks

問題

n頂点からなる木が与えられる。根は0番のノード。
各頂点には水槽がついている。お金をp円もっている。


最初にk個以下の好きなノードを選び、そのノードの水槽に1リットルずつ水を入れる。
次から全ての水槽が空になるまで以下の操作を繰り返す。

  • 水槽のふたを全部あける
  • 好きな水槽を(複数でもよい)選び、ふたを閉める。wリットル水が入った水槽のふたを閉めるのにかかる料金はw円
  • 根の水槽を空にする
  • 木の深さ1の頂点の水槽の水を、その親の水槽に移し変える。その水槽のふたが閉まっていたら飛ばす。親のふたは閉まっていても関係ない。


一つの水槽に(操作の途中の状態も含む)入っていた水の量の最大値を求めよ。

制約条件

n≦10^5
k, p≦10^9

方針

最大値をWとすると、どこかの頂点にWの水が入るタイミングがあれば、必ず根にWの量の水が入る時間があるので、
根に時間tに全ての水が集まると考えてよい。


時間tには深さtまでの頂点の水が来る。
深さiの頂点の水1リットルを根にとどめるためには、(t-i)円かかる。


なので、tを決めたら、深い順に貪欲に頂点を使っていけばよい。
tを1深くしたときのコストの差分は、ノードの個数だけで簡単に計算できるので、
あらかじめ深さiにある頂点の個数を求めておいて、
しゃくとり法を使えばO(n)で計算できる。

ソースコード

int n, lim_node, lim_cost;
vector<vi> e;

int cnt[100100];

void rec(int c, int p = 0, int d = 0){
	cnt[d]++;
	rep(i, e[c].size()) if(e[c][i] != p) rec(e[c][i], c, d + 1);
}

int main(){
	scanf("%d%d%d", &n, &lim_node, &lim_cost);
	e.resize(n);
	rep(i, n - 1){
		int a, b;
		scanf("%d%d", &a, &b); a--; b--;
		e[a].pb(b);
		e[b].pb(a);
	}
	rec(0);
	cnt[0] = 0;
	
	int ans = 1, nodes = 0;
	ll cost = 0;
	
	for(int i = n, j = n; j > 0; j--){
		
		while(i > 0){
			if(cost + cnt[i] * (ll)(j - i) > lim_cost) break;
			
			cost += cnt[i] * (ll)(j - i);
			nodes += cnt[i--];
		}
		
		int rem = (lim_cost - cost) / (j - i);
		ans = max(ans, nodes + min(rem, cnt[i]));
		
		nodes -= cnt[j];
		cost -= nodes;
	}
	ans = min(ans, lim_node);
	cout << ans << endl;
	
	return 0;
}