ABC341

6完。後半の問題は面白かったけど、結果については何も感想がないです。

A - Print 341

"10"をN回出力して最後に"1\n"を出力する。

問題名見てなかったけど、341を2進法で表すとサンプル1なんだね。

B - Foreign Exchange

問題文の長さ、入力の多さ、操作の順番によらないという考察、どれも難しくて150点じゃないだろ。順番に全部交換していく。答えが大きくなりそうと思って64bit整数で書き始めたが、Nが2*10^5なのでさすがにやばいと思い問題文を確認すると、交換して単位数が増えることはない制約だった。64bitで書いてしまったので何も考えず仕上げてそのまま提出したが、結局64bitは必要だったんだな。

C - Takahashi Gets Lost

全探索とはいえ重すぎる。こう書くと全探索が簡単みたいだな。全探索を書くのは難しいことが多いけど今回はそんなに難しくない。宇宙船が不時着するのは海だと思ったら陸だった。string("RDLU").find(t[i])で文字を数に変換。スタート地点が違えば違う場所に着くので、単に条件を満たす場所をカウントする。陸から始めて動きをシミュレーション、海に当たらず最後まで行けたら。

D - Only one of two

答えがどのくらい大きくなるかよくわかんなかったけどそこそこの頻度で現れそうだし10^18くらいには収まるんかな。実際の提出では2^62にしていた。二分探索する。i以下の正整数のうちNの倍数はfloor(i/N)個。包除すればいい。i以下の条件を満たす正整数が初めてK個になるところが答え。サンプル合わなくて、N*Mじゃなくてlcm(N, M)を使う必要があった(ひどい)。まだ合わなくて、サンプル1でなぜか6をカウントしていて、「ちょうど一方のみ 」ではなく「少なくとも片方」になっていた。手癖で書いてしまい気づけない。

E - Alternating String

A問題と似ている。奇数番目の文字を反転(0なら1に、1なら0に)して、全て同じ文字なのを良い文字列と呼ぶことにする。遅延セグ木を使わずにできるか考えたが思いつかないので遅延セグ木で書く。区間反転区間和取得。反転したら和は、その区間の長さから引いたものに置き換える。

解説を見て、まさか010101のまま処理できるとは。確かに、クエリ1で影響が出るのは2箇所しかないってなんとなく気づいてはいたんだよな。これを軽視していたという事実があまりにも重い。

(追記)BITで解き直したけど、これは添え字合わせが大変なので、思いついたとしてタイムやペナの確率が改善したかは微妙。

F - Breakdown

「有限回の操作の後には」のところ、1回も有限回だから1回の操作で必ずコマがなくなるのか?と。そういう読み方もできるよなと思った(しないけど)(今考えると1回じゃなくて0回のがいい)。

W_iを頂点の重みと呼ぶことにして、重みの和が小さいところへばらまくわけね。重みとコマの数の積の総和という量を考えると、(非負整数であり)操作で必ず減るので(しかも0になるのはコマが0個になったときのみなので)、確かに有限回の操作でコマがなくなるなと。重みが小さいところへしかコマを渡せないので、そういう辺だけ残すとDAGになる。DPできそう。ただ、どうやってコマを置く頂点の集合を選ぶのか。さっきの量がなるべく減らないようにとか思ったけど、あいまい。ある頂点にコマが1個だけあるとしてそこから何回操作できるかでDPすればよさそうだと思ったので実装。小さいほうからやるんだね。自分より重みが小さい頂点がない頂点は簡単。辺が5000しかないので、重み(これも5000)の回数のループを出てる辺の数だけやっても間に合う。なんとナップサックだ。重みがちょうどjのときの最大回数でやったけど、重みがj以下のときの最大回数でよかったよなあと。ナップサックの更新は重みが大きいほうからやればin-placeでできる。

(追記)書き忘れたけど、「重みとコマの数の積の総和」が最初64bit整数に余裕で収まるし、1回の操作で1以上減るから、操作回数もそんなに大きくならない。あと、最初常に0を出力していたが、渡す相手がいなくても(いても)1回は操作できることを忘れていた。「重みがj以下のときの最大回数」で書き直し、ナップサックのDPテーブルを全部1で初期化。

G - Highest Ratio

二分探索に見えるので、とりあえず計算量は無視して二分探索を書く。これをまとめてやったりできないかなあと考える。並列二分探索とか知らないのがけっこうマイナスに働く。それなりに時間はあったが、結局何もできなかった。

(追記)色々見てAC。計算量大丈夫かと思ったらスタックのやつね。累積和をプロットすると、kから「k+1からNまでのどれか」へ線を引いて傾き最大にする問題になる(全く思いつかなかったな)。kを考えているとき、k+1と「k+1からの傾き最大になる相手」の間(端を含まず)のことは考えなくていい。傾きがより大きいものはないし、その傾きが小さいなら相手はk+1で決まりだし、そうでないならk+1よりも1遠くから見ているのでより遠いほうがその傾きをたくさんもらえてよい。スタックを使わなくても単に相手を持つだけでスタックみたいになる(飛ばされたやつはもう現れることがないのでO(N))。傾きの比較は整数で簡単にできる(これが64bit整数に収まるようにするためにAが小さかったんだね)。まだちゃんとは理解できてないが、こうやってコードを書いて提出して少しずつ慣れていくしかない。