【AtCoder:15回目】AtCoder Beginner Contest 138の振り返り(Ruby)

【目次】

【本題】

振り返り

今回は 8/18(日)に開催されたAtCoder Beginner Contest 138の振り返りを行います。

AtCoder Beginner Contest 138 - AtCoder

今回は3問回答出来ました

レーティング微増

A - Red or Not

問題文 整数 a と、英小文字からなる文字列 s が入力されます。

a が 3200 以上なら s と出力し、 a が 3200 未満なら red と出力するプログラムを書いてください。

制約 2800 ≤ a < 5000 s は長さ 1 以上 10 以下の文字列である。 s の各文字は英小文字である。

条件が3200以上と未満の二つだけなので、三項演算子で簡単に出力を切り替えられますね。

作成したコードがこちらです。

a = gets.to_i
s = gets.chomp
puts a >= 3200 ? s : 'red'

以下の様にワンライナーで記述することも出来ますね。

puts (gets.to_i >= 3200 ? gets : 'red')

B - Resistors in Parallel

問題文 N 個の整数の列 A 1 , … , A N が与えられます。

これらの逆数の総和の逆数 1 1 A 1 + … + 1 A N を求めてください。

制約 1 ≤ N ≤ 100 1 ≤ A i ≤ 1000

この制約なら全探索で行けそうなので、以下の様にコードを作成しました。

n = gets.to_i
nums = gets.split(' ').map(&:to_i)

reverse_sum = 0
nums.each do |num|
  reverse_sum += 1.0 / num
end

puts (1 / reverse_sum)

以下の様にワンライナーで記述することも出来ますね。

gets
p 1/(gets.split.map(&:to_i).map{|i|1.0/i}.inject(&:+))

C - Alchemist

問題文 あなたは鍋と N 個の具材を持っています。各具材は 価値 と呼ばれる実数の値を持ち、 i 個目 ( 1 ≤ i ≤ N ) の具材の価値は v i です。

2 個の具材を鍋に入れると、それらは消滅して新たに 1 個の具材が生成されます。この新たな具材の価値は元の 2 個の具材の価値を x , y として ( x + y ) / 2 であり、この具材を再び鍋に入れることもできます。

この具材の合成を N − 1 回行うと、最後に 1 個の具材が残ります。この具材の価値として考えられる最大の値を求めてください。

制約 2 ≤ N ≤ 50 1 ≤ v i ≤ 1000 入力中の値はすべて整数である。

具材を足して2で割るという事は、

最大値のパターンを求める問題ですが、2で割る操作を多く行った値は、2で割る操作を行った回数が少ない値より、当然小さくなって行きます。

なので、小さな値から順に合成して行くのが、価値が最大になるパターンです。

これも全探索で行けそうなので、以下の様にコードを作成しました。

n = gets.to_i
vs = gets.split(' ').map(&:to_i)

vs.sort!
mixed_v = vs.shift

vs.each do |v|
  mixed_v = (mixed_v + v) / 2.0
end

puts mixed_v

これもワンライナーで行けますね。

gets
p gets.split.map(&:to_f).sort!.inject{|x,i| (x+i)/2}

D - Ki

問題文 1 から N までの番号がついた N 個の頂点を持つ根付き木が与えられます。 この木の根は頂点 1 で、 i 番目の辺 ( 1 ≤ i ≤ N − 1 ) は頂点 a i と頂点 b i を結びます。

各頂点にはカウンターが設置されており、はじめすべての頂点のカウンターの値は 0 です。

これから、以下のような Q 回の操作が行われます。

操作 j

( 1 ≤ j ≤ Q ) : 頂点 p j を根とする部分木に含まれるすべての頂点のカウンターの値に x j を足す。 すべての操作のあとの各頂点のカウンターの値を求めてください。

制約 2 ≤ N ≤ 2 × 10 5 1 ≤ Q ≤ 2 × 10 5 1 ≤ a i < b i ≤ N 1 ≤ p j ≤ N 1 ≤ x j ≤ 10 4 与えられるグラフは木である。 入力中の値はすべて整数である。

これも全探索でカウンターを回して行こうと考えましたが、途中で時間切れになってしまいました。

途中まで作成したコードはこちらです。

n,q=gets.split.map(&:to_i)

ab = []
(n-1).times { ab << gets.split.map(&:to_i) }

px= []
q.times { px << gets.split.map(&:to_i) }

result = []
n.times { result << 0 }

group =  ab.group_by { |i| i[0] }

px.each do |x, y|
  group[x]&.each do |g|
    result[g[1]-1] += y
  end
  result[x-1] += y
end

p result

但しこれだと、直接の結びつきがある頂点のカウンターしか値を足すことが出来ません。

そもそも制約的に全探索で出来る内容ではありませんでした・・・

こちらが模範解答です。

n, q = gets.split.map(&:to_i)
 
tree = Array.new
1.upto(n-1) do |i|
  ai, bi = gets.split.map(&:to_i)
  tree[bi] = ai
end
 
points = Array.new(n + 1, 0)
1.upto(q) do |i|
  pi, xi = gets.split.map(&:to_i)
  points[pi] += xi
end
 
2.upto(n) do |node_n|
  points[node_n] += points[tree[node_n]]
end
 
points.shift
puts "#{points.join(" ")}\n"