AtCoder Beginner Contest 116
Updated:
6 位でした。
Source codesPermalink
SolutionsPermalink
A - Right TrianglePermalink
コンテスト中に思いついた解法Permalink
直角三角形であることから、その面積は 3 辺のうち短い方から 2 つの辺の積の半分である。
恐らく模範解答Permalink
∠ABC=90∘ が保証されているので、 sort は必要ない。
x = gets.chomp.split(" ").map{|i| i.to_i}
puts x[0] * x[1] / 2
ポイントPermalink
問題文の誤読に近いが、安全策を取ったと考えれば許せる。
B - Collatz ProblemPermalink
素直に f を実装する。 set<ll> S;
を用意する。 a1=s とし、以降 ai+1=f(ai) を計算する。 S に問い合わせて存在すればその添字が答え。そうでなければ S に ai+1 を突っ込む。これを繰り返す。 1000000 回以内に終わることが保証されているのでこれで間に合う。
ポイントPermalink
ai≤1000000 が保証されているので int
でよかった。
C - Grand GardenPermalink
解法Permalink
0-indexed とする。貪欲法をやる。水やりをして花の高さを伸ばすのではなく、時を戻す魔法を使って花の高さを小さくしていくことにする。魔法を使って全部 0 にする最小回数を求める。
i=0,…,N−1 に対し、 h[i]>0 ならば、以下の動作を行う: j=i,…,N−1 に対し、 h[i]>0 ならば h[i] を 1 減らし、そうでなければ break;
する。ループを抜ける度に ans++;
する。これで答えが求まる。
貪欲法で良いことの証明Permalink
まず i 番目に対し h[i] を 0 にするためには、 i 番目の花に h[i] 回の魔法を適用する必要がある。そして 0 になったものに魔法を適用してはいけない(ルールの読み変える前だと h[i]+1 に成長してしまう)ので、ちょうど h[i] 回適用する必要がある。
魔法の適用する方法は有限なので、必ず回数の最小値が存在する。その最小値を実現する魔法の適用の仕方を 1 つ固定する。この魔法の適用の仕方で、 i=0 の花に対して適用した魔法の区間を順に [0,x0],[0,x1],…,[0,xh[i]−1] とする。ここで、貪欲な方法でない区間 [0,xk] が存在すると仮定する。つまり、この魔法を適用した直後に h[xk+1]>0 であると仮定する。するとこの魔法を適用した後に、必ず区間 [l,r] に適用する魔法が存在して、 xk+1∈[l,r] を充たす。したがって j∈[l,r] に対して h[j]>0 である。
しかしこのとき、 [0,xk] の代わりに [0,r] を適用し、 [l,r] の代わりに [l,xk] を適用しても、回数は変わらないし、魔法も適用できることがわかる。したがって最小値を達成しつつ、より「貪欲」な魔法の適用方法が見つかる。これを有限回繰り返すことで、結局 i=0 に関しては上記の貪欲法で最小回数が達成可能であることがわかる。
すると残りの問題は i=1 から先だけになるので、問題は小さくなっている。以降も貪欲法を適用し続ければ良い。したがって上記の解法で解答が求まる。
ポイントPermalink
証明は思いつく前に、直感で「これは貪欲法だ」で実装した。うまく実装できた。
D - Various SushiPermalink
0-indexed とする。
まず「おいしさ基礎ポイント」を貪欲に取ることを考える。つまり typedef tuple<ll, int> sushi;
とし、 {(d[i],t[i])} を vector<sushi> V;
に突っ込んで標準順序で sort
, reverse
しておく。 i=0,…,K−1 に対し、 d[i] の和をとる(X とする)。このとき、
cnt[i]= 今までに取ったネタ i の寿司の数
と定めておき、同時に計算しておく。 v=♯{i∈N∣cnt[i]>0} とし、まずひとまず A=X+v2 とする。これが答えの候補である。つまり 「種類ボーナスポイント」は「ひとまず貰えるもんはもらっておこう」 の姿勢である。
これ以上の得点を取るためにはどうすれば良いか? おいしさ基礎ポイント自体はすでに最大値 なので、 「おいしさ基礎ポイント X は減るけれども、種類ボーナスポイントは増えて、結果得点が増える」 以外に可能性はない。ここで「 X をできるだけ減らさない」けれども「 v は 1 増えている」という状態は、以下のように貪欲に求めることができる。
取った寿司を priority_queue<sushi, vector<sushi>, greater<sushi>> Q;
に突っ込んでおく。 i=K,…,N−1 に対し、以下の動作を実行する。まず cnt[t[i]]>0 ならば、これは v の増加に寄与しないので無意味であるから無視する。 cnt[t[i]]=0 の場合、検討する価値がある。 Q から寿司を取り出していくと、前段階で取った寿司をおいしさが小さい順に取り出すことができる。ここで (x,y) が取り出されたとする。 cnt[y]>0 は保証されている。
- cnt[y]≥2 の場合:寿司 (x,y) の代わりに寿司 (d[i],t[i]) を取ることで、「 X をできるだけ減らさない」けれども「 v は 1 増えている」という状態が達成される。 v,X,cnt は適切に動かし A←max(A,X+v2) とする。そして i を進ませる。
- cnt[y]=1 の場合:寿司 (x,y) の代わりに寿司 (d[i],t[i]) を取っても損をするだけである(v は変わらず x≥d[i] であるから)。ゆえに操作の対象ではない。
また Q=∅ になったらその時点で打ち切りである。以上により答えが求まる。計算量は O(N+KlogK) である。実装に priority queue を使う必要はもちろんなく、尺取り方の逆方向みたいにすることで O(N+K)=O(N) になる。
ポイントPermalink
ほぼほぼ、貪欲法だった。最初は種類数を固定すると思っていたけれども、うまくいかないようだった。 「ひとまず貰えるもんはもらっておこう」 に気付いてからは、 X と v のトレードオフができた。
OthersPermalink
A - sample: 3, tle: 2.000, time: 01:13, from_submit: 23:10
B - sample: 3, tle: 2.000, time: 03:35, from_submit: 19:34
C - sample: 3, tle: 2.000, time: 02:16, from_submit: 17:19
D - sample: 3, tle: 2.000, time: 17:19, from_submit: 00:00