ラベル 凸包 の投稿を表示しています。 すべての投稿を表示
ラベル 凸包 の投稿を表示しています。 すべての投稿を表示

2024年2月23日金曜日

トヨタ自動車プログラミングコンテスト2024#2(AtCoder Beginner Contest 341)

 Fまで。

コンテスト後のツイート

G - Highest Ratio

 解説放送を見てAC。

 「凸包」というキーワードを見ても分からず解説放送を見た。

 式から図形的性質を読み取るのは昔から苦手だったので解けなくても仕方なかったかな……とも思わなくはない。しかし、実験して、左端lに対して、右端rがどの位置で最大値を取るか? と考えていれば凸が見えなくても解けて欲しい気もする。

 いずれにせよ、難しい問題ではなかった。

2022年11月2日水曜日

AtCoder Beginner Contest 275

 Fまで六完だが遅解きだった。

コンテスト後のツイート

G - Infinite Knapsack

 解説放送を見てAC。

 凸包を使うのが久しぶりだったので、自分の昔の提出に取りにいった。(ライブラリに入れます)
 ただ、凸包の線分の両方の頂点が、y=xに対して片側にある場合に計算してはいけないことに気付かず、誤差のせいか? などと思ってWAを重ねてしまった。何を求めているかちゃんと考えないと。

2021年12月8日水曜日

AtCoder Regular Contest 130

 Bまで二完で終了。Bまで解いた後Fに行った判断が正解だったかどうか。

コンテスト後のツイート

C - Digit Sum Minimization

 公式解説は見ずにACしましたが、「繰り上がりの回数を増やせば良い」という本質情報を誰かのツイートで見た後でAC。
 「繰り上がりの回数を増やせば良い」ということが分かれば、まあ解ける。Pythonだと多倍長があるので、実際に数字を足して確かめることができるので楽ですね。

 コンテスト中は、桁DPかな、と思ったところで飛ばしてしまいました。
 「繰り上がりの回数を増やせば良い」は実験したら気付けた気がするので、この問題に集中していたら解けていた気もするけど、本当に実験コードを書こうと思ったかは分からないからなぁ……。
 

D - Zigzag Tree

 解説、解説放送を参照しつつAC。

 制約から二乗の木DPは考えたくなる。
 二乗の木DPは、「あるノードがルートだったとき、その部分木についての(何らかの)数」を持つDP。そのことが頭にあれば、「自分より小さい数が何個あるか?」を持ってDPすることが思いつける。これで(実際に実装しようとすると頭がこんがらがるけど、ちゃんとやれば)木DPは書ける。

 が、これを普通に書くと二乗にならず、累積和による計算量削減が必要になる。これは、式を見て冷静に考えれば分かるのだが、私はかなり悩んでしまった。

 木DPパートも計算量削減パートも典型といえば典型で、分かってしまえば難しいものではないし、解かなきゃいけない問題だとは思うけれど、実際にACまでもっていくのは結構困難に感じた。

F - Replace by Average

 コンテスト中に、a, $x_1$, $x_2$, ..., b(各$x_i$はaやbより大きい)という列に操作を行った最終結果がどうなるかは分かっていた。
 aの方が小さいとし、この列がn+1項とすると、(b-a)/nずつ等差数列のように大きくなっていく。余りはどうなるか? というと、1ずつbに近い方へと分配される。

 たとえば、
・3, 1000, 1000, 1000, 1000, 20
 に操作を行うと、
・3, 6, 9, 12, 16, 20
 のように、途中までは差が3、それ以降は差が4になって20へたどりつく。

 これは実験すれば分かった。
 公式解説ではこの証明に分量を割いていて、なかなか難しいが、問題を解くだけならこの結果を導くことができればOKだろう。

 こうしてできた結果が凸になることにはコンテスト中に気付いた。が、それを生かすことができず、値の小さい区間に対してこの操作を行う……みたいな方針へ走ってしまった。

 そこで凸包を作ろう、と思えたら良かった。
 こういうときアルゴリズムの勉強が大事ですね。凸包(convex hull trickやslope trickでも可)の具体的なアルゴリズムやイメージがちゃんと浮かべば、その方針を取れたと思う。

 凸包で得られた点たちに対して、その隣同士の点について上記の操作を行うと、かなり答えに近付く。実際、それを20回繰り返すことでACできた。(嘘解法だと思うけど、この回数がlogで抑えられそうな気もするので、もしかすると嘘解法じゃないかも?)

 正しい解法は、差が変わる点を適宜加えた(上の例だと、3と20だけでなく12も加える)凸包を作ればOK。
 1項目が3、6項目が20ならば商と余りを計算することで、「4項目が12になり、それが今作りたい凸包の候補になる」と分かる。この操作をしながら凸包を作っていけば良い。

2021年7月21日水曜日

エイシングプログラミングコンテスト2021(AtCoder Beginner Contest 202)

  Dまで四完でした。


E - Count Descendants

 深さごとにnodeを管理して二分探索、ということは思いつけていた。
 ただ、オイラーツアーを利用する発想がなかったため、LCAを用いて祖先がどこに当たるかを調べよう、という煩雑な方法を取ってしまった。
 一応、この方法でコンテスト後にACはできたので、ひどい方針だったという程ではないが、オイラーツアー(DFS順)の利用は見逃していることが多い気がする。

F - Integer Convex Hull

 公式から飛べる公式解説、ユーザ解説、解説動画を見てなんとかAC。

 典型090でAndrew's monotone chainを実装したことがあったため、公式解説の方法で実装した。(内部の点の個数の求め方はユーザ解説を読んで理解)

 ただ、確かにmonotone chainの方法を元にDPしているのだけど、ただ凸包を作るのではなく、前の点を全て試して凸になるか調べている、というのを理解するのに手間取った。

・start地点を全探索
・i→jと進む辺が凸包に使えるか全探索
・iの直前の点kを全探索(k→i→jと進んだとき、凸包になっているものを選択)

 するので四乗なのですね。

 そして、DPテーブルには、i→jが上側/下側凸包になる場合の、「総数」を入れる。つまり、全てのk(iの直前の点)に対して、k→i→jが凸包になりうるときの個数の総和を求める。ここで累積和的な考え方を使っていることも理解に手間取った。

 一応理解しACには辿り着けたけど、いや難しい……。