okimochi練(5/6)
国内予選2017と2015をやった。
2017
こるとんがAを通す。僕がBを見て書こうとするが詰まり、その間にCをこるとんに通してもらう。その間にBの誤読に気づき(おい)、落ち着いて通す。申し訳なかったけど実はこれはそこそこ苦戦するチームもあったそうなのでセーフ(いいえ)。
その後こるとんがFを難なく通す。その間にDもEもGもわからないと言っていた。こるとんがDはnm<=500が本質だと気づき、こるとんとrisujirohで通す。その後Gをrisujirohが思いつきこるとんが通す。Eをなんとか通そうとするが間に合わず終わり。僕は暇だったのでHを見ていたけど探索しましょうという感じだったのでまあ無理。
反省としては、Dの制約をちゃんと見るべきだった。ここは短縮できたと思うが、こういう問題もあるのか、と思ってしまった。本番9位相当で、まずまずと言ったところだった。The University of Tokyoが上位にいっぱいいた。
2015
このセットヤバすぎ。Aをrisujiroh、Bを僕が難なく通す。Cはこるとんが無理そうだったのでrisujirohが通す。その間にDはこるとんが思いつき実装、僕がその間にEFを見るがわからず、risujirohと一緒にFについてあーたこーだ言っていた。
DがTLEで計算量的にダメということになる。考え直してAC。その後も苦悶するがこるとんがEを通す(すごい)。Fもやろうとしたが実装方針が立たず終わり。
本番12位相当。Dは本番なら手元で回して結果を提出なので、ペナもタイムロスもないとすると5~6位になるらしい。Fの想定解を見て驚く(天才か?)。ちなみに別解として考えられていたものはコンテスト中に案があった。
Eくらいまでをできるだけ早く解くことが鍵になりそうという感触を得た。AOJ-ICPCの黄色まではしっかり埋めた方が良さそう、という話になった。普通に難しいとはいえもうちょっと仕事したい。
okimochi練(4/30)
5時間セットをheno_worldとやった。
セットはACM-ICPC 2016-2017 Asia region Daejeon(韓国セット)
Aを読むがわからない。
B:こるとんが簡単とか言うのでやってもらう、なんか入力形式がめんどくさかったみたいで2ペナ踏んでたけど通る。
risujirohがCをやる。実装が大変そうだった。すぐ通らなさそうだったのでbfsするだけだったGを僕が書いて通る。人のPCで少し打ちにくかった。
Iがやるだけらしいのでこるとんがバグらせながら通す。その後Cも通る。
Lが簡単な木DPじゃーんと思ったので僕が書く。普通に嘘を出したあと微修正して確信を持って提出するが通らない(は?)。さすがに意味不明なので2人を呼んでデバッグしてもらうが原因がわからない。stackoverflowを疑うがそうでもなさそうなのと、heno_worldも通っていないのを見てジャッジがおかしいと判断し撤退。ここに30分使ってしまって本当にもったいない。
Dをこるとんが頑張る。詰めきれずに1度risujirohがFをやるが、やはり詰めきれずDに戻って通す(すごい)。僕がHが解けたと主張したので書くが実装中に怪しいことに気づきこるとんと議論して木DPかーと思いつつ時間がなく諦める。Fも嘘だったとわかり終わり。
Aの考察をてきとうに言っていたがだいたい合ってたらしい。難しいね。
反省点
個人としては、Hがあやふやなまま実装に走ったこと。みんなで冷静にやれば解き切れたと思う。あと順位表から明らかに地雷なFをrisujirohに軽い気持ちで投げてしまったこと。(他が思いつかなさそうだったけどね…。)
チームとしては、力を注ぐべき問題を定期的に判断すること。あと、問題が難易度順じゃないセットは最初に全部に目を通すこと(G,Iが遅かった)。
ジャッジは仕方ない。Hも壊れてたらしいね。
エクサウィザーズ2019-D:Modulo Operations
めちゃくちゃ時間をかけてしまったけどこういうのが通せるようになったのは成長かな...。
考察過程・解法
確率に落とすのは苦手なので総和のままやった。
まず最初に,という2つの集合の要素について,で余りをとった値に,で余りをとってもその値は変わらないことに気付こう。したがって,をソートして新たに番号を振り直すことにする。すると,を作ってできた値をとすると,はたちの影響を受けない。このことを用いて,dpを組み立てていこう。
を「黒板にが書かれている状態から始めて,のみを選んで操作をしたときの,最後に黒板に書かれる数字の総和」としよう。こうすると,のについての総和が答えとなる。この理由を説明しよう。を先頭にすると決め打つと,次に書かれる数はとなる。そこからの操作はどうなるかというと,からまでの順番はの部分に表現されているから,残りの個の並べ方だけが自由である。従って,上の式が「を最初に選んだ時の,最後に黒板に書かれる数の総和」を表しており,これをについて足し合わせれば,すべての順列について考えたことになる。
最後にdpの遷移を考えよう。が黒板に書かれているとして,使える数がまでからまでに増えたときに,どのような操作が可能になるだろうか?まず,最初にを選ぶとすると,黒板の数はとなるから,その後の操作によってできる数の総和はもちろんとなる。最初に以下の数を選ぶとすると,その後の操作によってできる数の総和はとなる。がかかっているが,最初に以下の数を選んだので,はどのタイミングで選んでも総和には影響しない。よって,を入れるタイミングの数だけが加算されるということになる。
計算量はであり,C++なら十分間に合う。Pythonだと心配。
yukicoder contest 209:writerをしました
こんにちは。2019/3/22(金) 21:20~23:20のyukicoder contest 209(yukicoder contest 209 - yukicoder)
でwriterをさせていただきました。初めてのことで開始前は尋常じゃなく緊張しましたが,およそ160人の方に参加していただけました。ご参加ありがとうございました!
以下では,準備の話とかコンテスト中のおきもちとかをてきとうに書いていきます。
A:野菜が苦手
☆1が必要だなと思って作りました。野菜が苦手なのは本当で,特定の条件を満たさない限り野菜が食べられません(肉が十分量あるとかはその1つです)。最初はを許していなかったのですが,☆1ということでやめました。コンテストでは150人程度の方がACしていました。
B:UMG
バランス調整のために作りました。制約と計算量を意識しましょうという問題で,初心者向けとしては良かったのではと思っています。UMGというのは僕がいつも持ち歩いているウマゴン(UMAGON)のことです。他の問題文でも出てくるumgくんもそうです。こちらもおおよそ150人程度の方がACをしていました。
C:木を道に
今回のセットの中で最初に生えたのはこの問題でした。なぜ生えたのか覚えていませんが,この位置として良い問題だったと思います。証明を考えると☆2.5でもよかったかもしれませんね。
結局分岐の数を数えればよいということで,想定解はでしたが,で出している方も多かったです。よく考えると両者は一致するのでどちらでも良いです。
僕やtesterさんは最初にこの問題を考えたとき,木の直径でなんとかするのかと思っていたのですが,提出でもこれをやってWAという方がそこそこ見受けられました。提出を見ながらわかるなあ思いつつとにやけていました。コンテスト中のACは120人程度でした。
D:umg tours
ツーリストチケットと言いたかっただけなのですが,結果的に教育的な問題になったのでここに置きました。頂点に状態を持たせてDijkstra法を行うのはありがちな解法で,初めてという方にはいい練習になったのではないでしょうか。コンテスト中のACは70人程度でした。もう少し解かれると思っていましたが,頂点を増やしてから適切に辺を張る必要があったりなど,詰める段階や実装面では難しい部分があったと思います。
ところで,この問題はC++でも実行時間がかかり,恐らくこの問題が原因でコンテスト中にジャッジが詰まってしまいました。ジャッジについては少し運営の方と相談すべきだったかもしれません。また,Python勢は制限時間が4秒でもちょっと厳しかったと思います。制約を小さくするのはなんかなあと思ったので許してください。
E:Kaiten Sushi?
ボスとして用意しました。元々は「席が回る回転寿司屋で問題を作ろう!」というコンセプトで考え始めて(は?),ボツ問を生やしつつこの問題に辿り着きました。実は解に気付いて証明をつけるのに数時間かかり,またtesterさんも同様で,証明までやるのはかなり大変な問題,という想定でした。想定解としては,ある位置でのを見ると必要な最小の周回数がわかり,さらに最後に飲むことのできるお茶の位置も寿司とお茶の配置だけでわかるという,芸術点の高いものを用意していました。解けた方も解けなかった方も,是非とも解説を読んでほしいです。特にtesterさんは明示的に解を構成してくれました。
ところがコンテストでは,寿司とお茶の取り方について貪欲が可能で,多くの人にsetなどを用いて貪欲をシミュレーションする解法で通されてしまいました。こちら側ではこの貪欲のちゃんとした証明が生やせていなかったのですが,普通に正しいようです。コンテスト中にtesterさんや既に全完していたtさんと証明を検討していました。提出をちゃんと見ると,想定解に近い方法で通している人も見受けられたので,それは嬉しかったです。
結果的にコンテスト中のACは20人程度と,ボスとして十分機能したと考えています。解けた方も解けなかった方も,是非とも解説を読んでほしいです(大事なことなので2回書きました)。
そういえば,「最近開店したというKaiten Sushi」というギャグには気付きましたか?
全体を通して
難易度のバランスが不安だったのですが,結果的におおよそのAC者数が150-150-120-70-20だったのを見ると,良いバランスだったのではと思っています。DまではABCを意識,Eは暖色の人用という気持ちでしたが,狙い通りのセットになったと思います。コンテスト後は良セットだったという感想も多く見受けられ,嬉しい限りでした。
最後に
またセットでやるとしたら夏休み頃なのかなあと思っています。競プロの実力としてはまだまだなので,力をつけて作問のレパートリーが増えればよいなあと思っています。
時間があったので半分ノリで作問してしまいましたが,多くの人に解いてもらって,感想をいただけるのは本当に嬉しかったです。問題を見てくださったtesterの皆さん,参加者のみなさん,そしてジャッジシステムを提供してくださっているyukiさん,本当にありがとうございました!
第3回日本橋ハーフマラソン 予選
失敗したと思ったが(75位で,恐らくボーダーだが)予選通過したので,少し復習した。
A:ツーリストXの旅行計画
コンテスト中にやったこと
山登り/焼きなましでよさそうだと思ったので,という順から初めて,2点をswapする山登り法を試した。山登り法というのは
- 現在の状態から「少し変わった状態」のスコアを計算して,それが現在の状態のスコアより良ければ,「少し変わった状態」を現在の状態とする。
ということを繰り返すものである。この「少し変わった状態」を近傍と呼ぶ。2点swapというのは,点を訪問する順番を表した順列において,2点を入れ替えたものを近傍とするという意味である。これをすると28万点くらいになる。
Submission #4232922 - 第3回 RCO日本橋ハーフマラソン 予選
ビジュアライザの結果はこちら。
ところで,分散の計算には「」が使える。これを用いれば,実は近傍のスコアの計算をで済ませることができる。つまり状態として訪問順の他に,距離の総和と距離の2乗和をもっておけば,点を入れ替えたときに減る距離と増える距離をそれぞれ計算することができて,これは高々4箇所であるので,で更新が済むというわけである。これは差分更新と呼ばれたりする。
しかし,これをやればスコア伸びるでしょーと思って出したのだが,なんと全く伸びない。このあとBを解き,Aに戻ってきてからいろいろと試したが,結局28万点のまま終了した(152位)。悲しいね。
反省会
山登り/焼きなましというのは悪くなかった。問題は近傍の選び方であった。2点swapでは,パスを構成する辺が4つ変わっていることになる。これは全然「少し」変わった状態じゃないという問題があった。ここから,近傍として「パスのうち2辺を変更する」ということを考える。これは2-optと呼ばれる手法(?)らしい。そんなこと簡単にできるのかとちょっと思うが,訪問する順番の順列のある区間をreverseするだけでできる(絵を描くとわかる)。
なんとこれだけでスコアが150万点くらいになる(!?)。およそ5倍になっている。コンテスト終了後,TwitterのTLでは,「スコアが5倍になった」というツイートが散見された。
Submission #4238797 - 第3回 RCO日本橋ハーフマラソン 予選
ビジュアライザはこちら。大幅に改善している...。
この提出は差分更新をしていないのだが,2点swapのときと同様に差分更新にして,焼きなまし法に変更すると,220万点が出る(30位相当)。焼きなまし法はパラメータの調整が面倒なのだが,以下の記事を参考にいろいろ試したらうまくいった。ありがとうございます。
焼きなまし法の適用メモ - Negative/Positive Thinking
提出はこちら。
Submission #4276320 - 第3回 RCO日本橋ハーフマラソン 予選
ビジュアライザはこちら。
上位陣の考察を見ていると,使う辺の長さの範囲を限定したり,ある長さを決め打ってその長さに近い辺を使うということをしていた。この長さを決め打つという手法は有効らしく,「長さを乱数で決め打って,決め打った長さに最も近いものを選びながら,先頭から順番にパスを構成していく」だけで80万点が出るらしい(自分は試していない)。マラソンわかりません。
B:ファーマーXの収穫計画
コンテスト中にやったこと
大きい数字である9や8をたくさん作って収穫したいと思った。ところが,1とか2を9にしても手数がかかるのでよくなさそうだ。そこで,てきとうに閾値を決めて,閾値以上のマスは収穫したい数字にする,ということをした。閾値は収穫したい数字の半分以上とした。収穫したい数字は9から始めて,9を収穫しきったら8を試す....ということを繰り返した。実装上はBFSを何回かすることになる。なんとこれだけで54万点が出る。
さらに,9のマスはいくつかあるわけだが,収穫する順番を乱数で決め,時間いっぱい試すことにより,55万点に乗った。これが最終スコアになった(38位)。やったことは非常にシンプルだが,これでそこそこ上位が取れたのは助かった。
Submission #4236103 - 第3回 RCO日本橋ハーフマラソン 予選
ビジュアライザはこちら。ほとんど9で収穫している。よく見ると8も少しある。
反省会
大きい順にとる,というだけでは,手数の問題をまだ十分に考慮しきれていない。ある区画を収穫するときに,その区画を収穫すると1手あたり何点得られるか?ということを考えるのは,言われてみれば自然なことのように思える(まあ気づけなかったんですが...)。そこで,「ある区画を収穫するときに得られる得点/その区画を収穫するまでにかかる手数」を評価値とする。このとき,「その区画を収穫するまでにかかる手数」は小さくしたいから,収穫するマスの値をとすると,「隣接するマスの値にするのに必要な手数の最小値」を求める必要があるが,これはbfsの際に優先度付きqueueを用いることで可能になる。
すべてのマスについて評価値を計算した後,それが大きい順に実際に手入れ,収穫をする。評価値が最大でないマスは,すでに本当は使いたかったマスが収穫されていしまっているということがあり得るが,その場合は評価値を再計算する。これを手数の限り繰り返す。
なんとこれをすると60万点に乗り,2位相当となる(!)。何を評価値すればよいかということをしっかり考えることが重要だと思わされた。
Submission #4267434 - 第3回 RCO日本橋ハーフマラソン 予選
ビジュアライザはこちら。要はコスパの良い順なので,さまざまな数字が収穫されている。また,収穫されているマスが各段に増えている。
せっかくなので本戦も頑張りたい。
AGC009-C:Division into two
きれいな問題だなあと思った。
解法
簡単のためとする。逆のときはswapしても問題ない。こんなのdpしかないやろという見た目がするので,dpを考える。制約を無視すれば,今何番目の数を見てるかということと,またはに最後に振り分けた数はどれだったかということが必要に見える。そこでを「番目まで振り分け済みで,最後にに振り分けた番号がのときの振り分け方の総数」とできる。遷移は,①ならからに遷移できる。また,として,について,②かつ,からの間の項間の差がすべて以上であるときに,からに遷移できる。(なぜからかというと,でも,をに振り分けているから,はに振り分ければ問題ないからである。)もちろんこれを愚直にやるとで,到底間に合わない。
よく考える。まず,「からの間の項間の差がすべて以上である」かということをdpの更新のときに逐一調べる必要はない。つまり,であれば,より手前の数は,これ以降②タイプの遷移が不可能だとわかるので,そういう番号を管理する変数を持っておけばよい。これをとし,定義を「現時点で②のタイプの遷移が可能な番号の最小値」とする。さらに,をみたす最大のを二分探索し,それをとすると,に遷移してくるのはのつ目の添え字がからまでの数である。だから,累積和を取っておけば②のタイプの遷移はで可能である。
タイプ①の遷移だが,これはつ目の添え字が増えるだけである。タイプ②の遷移も,つの目の添え字は増えるだけだから,結局を「最後にに振り分けた数の番号がであるときの振り分け方の総数」として,これをが小さい順に埋めれば十分である。最後に,明らかに答えがになるパターンの存在に気を付けよう。それは,あるが存在してとなることである。をにもにも振り分けることができなくなるからだ。
以上より,次のようにすればよい。
- となるがあったら答えは。
- を「最後にに振り分けた数の番号がであるときの振り分け方の総数」とし,とする。また,を「現時点で遷移してくることが可能な番号の最小値」とし,とする。が小さい順にこれを埋める。最初に,とする。をみたす最大のをとし,であればとする(のときは遷移できない)。最後に,であればとする。
- 答えは,である。
実装
Submission #4183566 - AtCoder Grand Contest 009
二分探索のとき,のときの扱いに注意。あとのときも注意。
ちゃんと考察すると綺麗な遷移でコードも短い,好き。
CODE THANKS FESTIVAL 2018-H:Median Game
これはわかればシンプルで楽に解けるので好き。
解法
このゲームでは,ある列の中央値がゲームのスコアになり,それを求めよと言われている(偶数のときは2つ候補があるが,大きいほうをとる)。中央値を求める問題では二分探索が有効である。中央値が以上となるか?という質問に答えるには,数列の中により以上の数が半分以上含まれるかを調べるだけでよく,これは比較的容易にできるためである。
それでは,「ゲームのスコアは以上になるか?」という問題に答えよう(この問題はについて答えが単調なので,二分探索が使える)。このとき,Aliceはスコアを大きくしたいので,以上の数を増やしたいはずである。一方Bobは,スコアを小さくしたいので,より小さい数を増やしたいはずである。この2人の戦いをdpによって見守ろう。
- :上から枚目以降のカードが残っているときにAliceの手番で,の最大値
- :上から枚目以降のカードが残っているときにBobの手番で,の最小値
dpの遷移であるが,とりあえずAliceの手番のときを考えよう。今回はこのdpにかけてよいので。について,区間のカードをとり,枚目以降のカードを残してBobに手番を渡すとしよう(のときはゲーム終了という意味)。これらのカードの総和をとすると,との大小によってにを足すかを足すかすればよい。この結果のについての最大値がである。Bobの手番のときも同様である。
dpの遷移は後ろから,つまりから行うことに注意。ゲームでdpをするときの定番だろう。「ゲームのスコアは以上になるか?」という問題の答えは,のときYes,そうでないときNoである。
実装
Submission #4127440 - CODE THANKS FESTIVAL 2018
実装は軽い。dp配列の初期化だけ注意。
本番はほとんど問題文を読まなかったが,もったいなかったかもしれない。