JOI2020本選 参加記

去年
ta1sa.hatenablog.com

0日目

夜に謎の熱っぽさに襲われ、頭も痛かったので厳しい気持ちになった。

1日目

朝起きたら体調がマシになっていたので、難易度9バチャをする。しかし、1年前に自力で解けたTentsという問題が全然解けず、冷えた。
普通に一人で電車でつくばに向かった。去年と同じように毒蛇の脱走を考えていたが、小課題3以降の進展は結局なかった。
つくば駅ではラムネを買った。これが後の運命を左右することとなる(ほんまか?)
会場まではあまり迷わなかったが、途中つくばカピオに無意識に向かいかけてしまった。2年前にTwitterに大量に流れてきてたからしょうがないね。

行くと知っている人が結構いたが、この時点で体力をかなり使ってしまっていたので席でダラダラしていた。
ラクティスは、全完した後にEmacsのAdventureというゲームをやってみようとしたが、何を打ち込めばいいのかよく分からずまともにできなかった。
講義でOSSへの理解を深め、夕食では、炭水化物を大量に摂取した。ここでもあんまり積極的に交流はしなかった。人々の若さを感じた。

宿に着いた後はTentsを通して自信をつけようと思っていたが、スマホを入れた上着を脱ぎ捨てた際に独房のベッドの角にぶつけたらしくスマホの画面が割れ、とても悲しくなった。本選1週間前にPCの充電器とイヤホンを壊したばっかりだったのに...。
それを煽りに来た人々とわちゃわちゃした(なぜか名札が消え去った)。なんだかんだでその夜は色々な人と話した気がする。あとTentsは自分の方針だと先に進めないと知ってしまった。
22:30くらいに消灯したが、その後は体調の悪さ+緊張で体が火照ってしまい、ひたすらつらかった覚えがある。気がついたら眠っていて、朝だった。

2日目

6:15くらいに起きて外を見ると夜明け前で、空がグラデーションしていてなかなか良かった。朝食は、THE 日本食という感じで、質素だが自分は割と好きな感じだった。
気分を上げるために音楽を聴いたりし、本選会場に向かった。
開始まで謎の待機時間があり、その間に頭が痛くなってしまって険しかった。開始5分前からはワクワクの気持ちが勝っていた。

競技が開始すると、問題を見る前に諸々の前準備を済ませた。

1問目はすぐに貪欲が見え、証明もできたが、自分の頭を信用していなかったので部分点から取りにいった。あまり迷わず、22:12に満点。

2問目は、しばらくまともな解法が思いつかなかったが、「尺取り」というキーワードが思い浮かぶとすぐに解けた。47:54に満点。

3問目は、小課題1つ1つの意図がかなり明らかだったので、正当性を犠牲にまずDPテーブルを考えた。そして普通に正当性が分かったので実装すると、一発で満点が取れて感動した。74:50に満点。

4問目は、小課題1,2は割とすぐに分かった。メダルを取る気だったので満点を考えたが、不可能に見えた。そこで、安全策を取ろうと小課題1,2を先に実装したが、小課題2しか取れていない(129:07)。
「高々1つ」の誤読に加え、様々なバグを埋め込んでいたので、小課題1が通ったのは5問目の部分点を取った後、172:36のことだった。

5問目は、見た目は仰々しいが、問題文を読むと好きなタイプのクエリゲーであることが分かる。それぞれの小課題も意図が明らかで、ほぼ考察することなく分かってしまった。全人類これ取れてるんだろうなーと怖くなった。
RMQセグ木とBITを生やし、ほぼバグらせずに163:16に20点。

4問目の小課題1が最後の得点となったわけだが、そこから先は、5問目の満点 or 4問目の小課題3 で迷い、残り1時間であることを考えると、どう考えても実装が重そうな5問目は選ばないべきだと考えて4問目の小課題3をひたすら考えていた。
しかし、体力・集中力のなさからあまり考察に身が入らず、嘘解法を生やして悲しくなっただけだった。ただ、解説によるとあまり遠い方針でもなかったらしい。
もっとも、ラムネがなかったら3問目を通した時点で力尽きていただろう(伏線回収)。糖分大事。

結果は 100+100+100+16+20=336 という人間に取れる問題を全て取りましたねという点数だった。実際、自分の取ってない小課題はメダリストしか取っておらず、取った小課題は大量に通されていた。
去年も同じような取り方をしていて、結局1時間以上残してしまったので、成長を感じられなかった。

競技終了後は昼食だが、去年と同様お通夜ムードだった。治安は去年よりは良かったが、ボーダー予想が飛び交っていたのは変わらなかった。どうやら300前後らしい。
あと、自分より高い点数の種類数が3以上であることが確定し、悲しくなった。去年は3位だったが銅賞がもらえず、今年こそメダルが欲しいと思っていたからだ。
ただ、多分春合宿に通ったということで、若干の安心感もあった。シンガポールに行きたいのはもちろんだが、春合宿に行くことで得られるものはとても多いと思っていた。

解説と自分の感触を総合して、今回の難易度は 6-7-8-11-11 のようだった。4も5も完全に不可能な問題ではなかっただけに、自力で解けなかったのが悔やまれる。

今年はカラオケには行かず、普通に帰った。帰りながら4,5の考察をしようと思ったが、疲れからかあまり身が入らなかった。

反省

1-3のムーブはそれなりに理想的だった。1で部分点を実装せず、2をもう少し早く考察できればよかったという感じ。
問題はそこから、使う頭が変わってしまったというところにある。
1-3を解いている時は、脳をフル回転させていたという感覚があった。しかし4を解いている時は、意味もなく図を書いて考察した気になり、まともに頭を使えている時間が少なかったという感じがある。5に関してはそもそも満点は不可能だと思ってまともに考えなかった。
これは、難しい問題を、解けると信じて解いている時のモードで解く経験が全くなかったということと、体力不足に起因すると考えられる。
ただ難しいところは、満点を取れると信じて部分点の考察をおろそかにするのはかなりリスキーだということ。つまり、全ての小課題をまともに考察できる体力・集中力が必要。
春合宿までにこれをなんとしてもなんとかしたいので、色々やってみようと思う。(決死の十時間バチャ?流石にやりません...。)

10/27-11/9

文化祭でまともに競プロができていなかったので2週まとめて。
AtCoderのレートがいっぱい上がったしHTTFもユース枠で通ったのでよかった。

出たコンテスト

AGC040 90位 2043->2111
HTTF2020予選 73位
第2回全統プロコン予選 102位 2111->2163

解いた問題

ABC144-F Fork in the Road

https://atcoder.jp/contests/abc144/tasks/abc144_f
消す辺を全探索すると間に合わない。Nが小さいので、頂点ごとにそこから出る中でどの辺を消すのが最適かがわかればよい。
Nから逆順に「dp(i):iからNまでの通る辺数の期待値」をO(M)で求める。すると、各頂点ごとに、向かう先のdpの値が最大であるような辺を使えばよいと分かる。dpの遷移を考えると明らか。
これで消す辺の候補がO(N)本になったので、あとは各場合について愚直に計算すればO(NM)で解けた。

CF1246B Power Products

https://codeforces.com/contest/1246/problem/B
なんでコンテスト中に解けなかったんだろう...。
条件は、「ai*ajの素因数の指数が全てkの倍数である」ということに言い換えられる。
各aiについて、「根」を「aiの各素因数の指数をmod kしたものの積」とおくと、ai*ajが条件を満たすようなajの「根」が一意に定まる。よって、各aiについて素因数分解して「根」を求めれば、O(N√maxa)で解けた。

ABC020-D LCM Rush

https://atcoder.jp/contests/abc020/tasks/abc020_d
S(i):gcd(j,K)=i なるjの和(iはKの約数,1<=j<=N) とする。Kの約数の個数をLとおく。
T(i):iの倍数であるようなjの和 とすると、O(L)で全てのTが求まる。
S(i)=T(i)-(iの倍数jについてのS(j)の和) となるので、iを降順に見ていけば、O(L^2)で全てのS(i)を求められる。
lcm(i,K)=i*K/gcd(i,K) から、答えがS(i)*K/i の和だと分かるので、全体でO(√K+L^2)で答えが求まる。

第4回 ドワンゴからの挑戦状 予選-E ニワンゴくんの家探し

https://atcoder.jp/contests/dwacon2018-prelims/tasks/dwacon2018_prelims_e
重心分解をすることを考える。
次のような戦略でできる。
・今の部分木の重心を取り、その子をサイズの大きい順に並べる。
・2つずつ調べ、返ってきた方の部分木に潜る。
・もしなければ重心が答え。
サイズに大きい順に見ているのがミソ。
1回で見つかれば木のサイズが1/2以下になる。
2回で見つかれば木のサイズが1/3以下になる。
3回で見つかれば木のサイズが1/5以下になる。
全部3回必要になるケースで通るか怪しいが、N<=2000なおかげでギリギリ通る。

JSC2019予選-E Card Collector

https://atcoder.jp/contests/jsc2019-qual/tasks/jsc2019_qual_e
Ri-Ciグラフを考えると、「全頂点の出次数が1を超えないように向き付けられるような辺集合について、重み和の最大値を求めよ」という問題になる。
実は、各連結成分について「辺数<=頂点数」を満たす時が最適。これは木+1辺、または木となるので、適当に向き付けていけば上の条件を満たすことがわかる。逆に、それ以上辺が増えると条件を満たすことがない。
後は、辺を重みの大きい方から見ていき、加えても条件を満たすなら加える、というクラスカル法のようなことをすればよい。正当性はクラスカル法と同様に示せる。

第二回全国統一プログラミング王決定戦予選-D Shortest Path on a Line

https://atcoder.jp/contests/nikkei2019-2-qual/tasks/nikkei2019_2_qual_d
最終的な最短路を考えた時に、辺の張り方から、「逆行」がないようなものが必ず存在。つまり、DAGとして考えてよい。
dp(i):iまでの最短路の長さ というDPをする。区間を終点でソートして考える。
ある区間[L,R]を見ている時、実はdp(R)のみ更新すればよいことが示せる。
遷移は、dp(R)=min(dp(k)+C)(L<=k < R) となり、これはセグ木でO(logN)ででき、全体でO(NlogN)で解けた。

感想

文化祭休み最高!

10/20-10/26

1ヶ月続いたけど既にさぼりそうな気配が出てきている。
競プロで冷えただけの週だった。頭の疲労を効率的に取れるようになりたい。任意のコンテストが塾の後にあるのでまともに頭が働かない...

出たコンテスト

CF#596(Div.1) 589位 1938->1866(???)

IOI2014 Day 1 virtual

30+32+100=162
激冷え。最後の1時間でなんとか息を吹き返した感じ。
0:20 1問目の読解が難しかったので、確認のため8点を取る。
0:31 1問目の30点がすぐにわかったので取る。
~1:30くらい 2問目がセグ木ゲーっぽく見えたので考えると、嘘セグ木解法が生えるのでひたすらそれを詰める。
~2:00くらい 2問目がわからなくなったので3問目に行くが、小課題1もわからない。
~3:00くらい 脳が疲れて何も考えていなかった。3問目の誤読に気づく。
~3:30くらい 部屋を歩き回っていると、3問目の方針が分かる。
3:57 3問目の42点が通る。若干バグらせた。
4:08 3問目の満点に取り掛かる前に自明を通しておく。
4:32 N=1200でO(N^3)のつもりで書いていると、実はO(N^2)であることが実装中にわかる。3問目が通る。
4:47 2問目の小課題2は普通にセグ木で解けることがわかったので通す。
難易度は11-10-10みたいな感じに思えた。

1問目 Rail

解けていないし解説も見ていない。

2問目 Wall

解説を見た。合成は無限に考えていたがこの発想はなかった。
実は、ある数への任意のchmin,chmaxの操作列は、chmax(chmin(x,a),b)の形で表せる。これは帰納的に考えると分かる。
具体的には、
chmin(chmax(chmin(x,a),b),c)=chmax(chmin(x,min(a,c)),min(b,c))
chmax(chmax(chmin(x,a),b),c)=chmax(chmin(x,a),max(b,c))
のように合成できる。よって、(a,b)をセグ木の各ノード上で持っておけば、適当な遅延伝播で最終的な答えが求まる。

3問目 Game

「最後の辺が異なる連結成分を繋ぎ、それによってN頂点が全て連結になる」という条件を満たせばよい。
ある辺を繋ぐことによって、最後の辺が同じ連結成分を繋いでしまう可能性が少しでもできたらダメ。
そのためには、(u,v)を繋げる辺を使う条件を、
・u,vは同じ連結成分内にない
・u,vの連結成分を繋げる(u,v)以外の全ての辺を既に見ている
とすればよい。明らかに最後の辺が同じ連結成分内の頂点を結ぶことはなく、これで必ず最終的にはN頂点が連結になる。
これを愚直に実装するとO(N^4)となり、42点が取れる。
C(i,j):連結成分iと連結成分jを結ぶ辺の本数 というテーブルを計算することを考える。これがわかればO(1)で条件判定ができる。
1回の更新でO(N)かかるが、連結成分のマージはO(N)回しかないので、O(N^2)で解けた。
意外と条件が厳しいので、できることが限られる問題。

解いた問題

技術室奥プログラミングコンテスト#2-E 歩くNPCたち

https://atcoder.jp/contests/tkppc2/tasks/tkppc2016_e
ある定数Bを考え、Vi < BとVi >= Bで場合分けする。
・|Vi| < Bのとき
同じ傾きの直線同士の順序は変化しないことに注目すると、「ある傾きの直線の集合についてのクエリ」には二分探索でO(logN)で答えられる。
傾きの種類数はO(B)だから、このようなクエリには全体でO(QBlogN)で答えられる。
・|Vi| >= Bのとき
Rの上限をmaxRとおくと、直線iは、T <= maxR/Viなるクエリでのみ考慮すればよい。
各直線について愚直にクエリに答えたとしても、考慮すべきクエリの数から、全体でO(NmaxR/B)で答えられる。
これで、計算量がO(QBlogN+NmaxR/B)となる。B=100などにすれば間に合う。

CODE FESTIVAL 2018 qual A-D 通勤

https://atcoder.jp/contests/code-festival-2018-quala/tasks/code_festival_2018_quala_d
「建て替えない集合」の個数を求めればよい。
実は、「補給する場所の組み合わせ数」をベースに考えればよい。なぜなら、補給場所の集合が異なるならば必ず建て替えない集合も異なり、被りが起こらないからである。
dp(i):iで最後に補給することになるようなそこまでの建て替えない集合の組み合わせ数 というDPを考える。
このとき、dp(j)がdp(i)へ寄与する条件は、F-T < Xi-Xj <= F⇔Xi-F <= Xj < Xi+T-F というような区間になり、この前計算は二分探索でO(NlogN)でできる。
また、寄与する値は、Xj+F-T < Xtを満たす最も左のtについて、2^(t-j-1)*dp(j)となる。
これで累積和を用いてO(N)で計算でき、後は答えを適当に求めばよい。最後は区間の右端条件が必要ないことに注意。
これでO(NlogN)で解けた。

10/13-10/19

今週はvirtualはお休み(毎週お休みしそう)。
CFでレートを下げただけの週だった...。

出たコンテスト

KUPC2019 34位
CF#592(Div.2) 372位 2017->2008
CGR#5 2498位(????) 2008->1908
CF#593(Div.2) 335位 1908->1938
ABC143 23位

解いた問題

KUPC2019-D MaxMin Game

https://atcoder.jp/contests/kupc2019/tasks/kupc2019_d
i+1回目に勝ち負けが入れ替わったとする。このとき、i回目までに1以上2i以下のカードが全て手札から出ていることが必要。
よって、入れ替わるタイミングでは最初がどんな手札でも出ているカードの集合は同じなので、勝ち負けが変化しないようないくつかの区間に分割して考えられる。
そのような長さkの区間内で2人にカードを分配する通り数は、「最終的に|A|=|B|となり、常に|A|>=|B|を満たすように、前から集合A,Bに分配していく通り数」となる。これは実は、長さkの括弧列の数と同じである。これはカタラン数として知られており、C(2k,k)-C(2k,k-1)となる。
これを全ての区間について求め、掛け合わせれば答えが求まる。

KUPC2019-F カズマ王国の陥落

https://atcoder.jp/contests/kupc2019/tasks/kupc2019_f
各拠点から、ある1つの街だけに派遣するような最適解が存在することが示せる。
区間を右端でソートし、次のようなDPを考える:
dp(i,j):i番目の区間まで見て、今まで派遣した中で最も右にある街がjのときのダメージの最大値
右端が単調増加しているので、遷移元が狭まることがなく、このDPで最適解を求めることができる。
遷移にO(N)かかるように見えるが、累積maxを取っておくことでO(1)ででき、O(NM)でこの問題が解けた。

Mujin Programming Challenge 2017-B Row to Column

https://atcoder.jp/contests/mujin-pc-2017/tasks/mujin_pc_2017_b
明らかに、全てのマスが黒になるまでに初めてある1行が全て黒になる瞬間がある。これをi行目とし、iを決め打った時の全て黒になるまでの最小操作数を求める。
(i,j)が黒くなっていないとき、(k,i)が黒いようなkが存在するならば、(i,j)は1回で黒くできる。もしこれが存在しないなら、マス目上に1つでも黒いような(l,k)が存在すれば、最初に(l,k)->(k,i)と1回操作しておくことで、1回で(i,j)を黒くすることができる。これを全てのjについて考えれば最小操作数が求まる。
実は、これに「元から全て黒くなっていないような列の数」を足したものが答えとなる。明らかに、i行目を黒くする過程でそのような列数は増えることがない。また、実は減ることもないことが示せるので、これがiを決めた時の最適解となることがわかる。
これを全てのiについて計算するので、適当な前計算をすればO(N^2)で解ける。黒いマスが存在しないときは-1を出力すればよい。

天下一プログラマーコンテスト2016予選B-C 天下一プログラマーコンテスト1999

https://atcoder.jp/contests/tenka1-2016-qualb/tasks/tenka1_2016_qualB_c
本戦の順位によって人を並び替えておく。以下、ハシモトくんの記録上の勝ち負けを考える。
dp(i,j):i番目の人まで順位が一致していて、i番目の人がj回勝ったような確率 というDPを考える。
遷移は、dp(i,j)=sum{dp(i-1,k)}*f(i,j) のようになる。予選順位によってkの下限がjかj+1か決まる。f(i,j)は「i番目の人がj回勝つ確率」であり、これは各iについてO(N^2)で計算できる。dp(i,j)の遷移はO(N)でできるので、全体でO(N^3)で解けた。

ARC067-F Yakiniku Restaurants

https://atcoder.jp/contests/arc067/tasks/arc067_d
訪れる中で番号が最小の焼肉店をL、最大の焼肉店をRとおくと、LからRへ順に移動していくことで、移動距離の最小値を達成できる。よって、L,Rを決めれば、区間[L,R]における焼肉の美味しさの総和を最大化すればよくなる。
各チケットは、区間内で自由に使う場所を決められるので、区間内でもっともBの大きい場所で使えばよい。
Segtreeを使えばO(MlogN)で区間を決めた時の美味しさの総和の最大値がわかるが、全体でO(N^2MlogN)となってしまい間に合わない。
f(L,R):区間[L,R]における焼肉の美味しさの総和の最大値 というテーブルfを計算したい。
焼肉(i,j)が食べられるような区間[L,R]の条件を考える。全ての(i,j)についてこれが求まれば、f(L,R)にB(i,j)を足していくことで全てのf(L,R)が求まる。[L,R]でのチケットjで食べられる焼肉の中でのBの最大値がB(i,j)となればよい。
この条件は、「S<=L<=i,i<=R<=Tを満たす任意の[L,R]」という形になる。S,Tは、setを持ちながらBの小さい方から見ていくことで、各チケットごとにO(NlogN)、全体でO(MNlogN)で求められる。Bが等しいもの同士も、適当に順序付けすることでうまく処理できる。
後は条件を満たす[L,R]について、B(i,j)を足しこめばよい。
二次元平面上で考えると、長方形領域に値を加算していく形になっている。これは二次元imos法でO(NM+N^2)でできる。
これでf(L,R)が求まったので、この問題が解けた。

ABC143-F Distinct Numbers

https://atcoder.jp/contests/abc143/tasks/abc143_f
Ci:「Aj=iなるカードjの個数」とする。
X回操作すると仮定し、これが可能である条件を考える。問題は、次のように言い換えられる。
「X行K列のマス目に、数列Aの中から1つずつ数を書き込んでいく。同じ行に同じ数が入れてはいけない。マス目を全て埋められるか?」
明らかに、Ci>=Xなるiは、iで一列埋めてしまうのが最適。
後は、残っている列をCi=Xなるiの個数)*X+(Ci=X*K」が条件だとわかった。
単調性より、後はXを二分探索すれば答えが求まる。
累積和などで必要な値を適当に前計算しておけば二分探索内の判定はO(1)でできるので、全体でO(NlogN)で解けた。

感想

学校の後のCodeforcesは必ず仮眠を取る!

10/6-10/12

出たコンテスト

🤔

IOI2013 Day 2 Virtual

今までやった5時間セットで一番成績がよかった。
100+100+63=263点、Day1と合わせて430点、銀の真ん中らへんなので適正という感じ。
~0:30くらい 1問目を読むと、30分で76点、定数倍が軽ければ100点が取れる解法がわかってしまう。
76点でも取っておいたほうがいいと思ったので実装する。
0:47 1WAを出したが、1問目が通る。にぶたんの区間に気をつけよう。
~1:20くらい 2問目を読むと、よい性質に気づいて一気に90点、定数倍が軽ければ100点が取れる解法が15分くらいで分かる(定数倍ゲー多い...)。
1:50 書いたコードの定数倍が軽かったらしく、2511ms/3000msで通る。
~2:10くらい 明らかに「動的二次元セグ木を書きなさい」という問題だが、闇な気がしたのでやる気をなくす。とりあえずただの二次元セグ木で取れる63点を取ることにする。
2:22 3問目の63点が通る。動的二次元セグ木を導出しようと頑張る気が起きなかったので撤退。(時間が半分で終わってしまった...)

難易度はたぶん8-9-11みたいな感じ。
1問目 Cave
小課題3までは省略。
ドアを左から見ていき、順に対応するスイッチとその正しい状態を当てることを考える。
制約を見るとlogがつきそうなので、二分探索を考える。
「対応が未決定のスイッチの中の左からX個選んだ集合について、今見ているドアと対応するものがあるか?」という問題が解ければ、Xについて二分探索することで答えが求まる。
実はこれは、「左からX個選んだ集合」のスイッチを反転させ、元の状態での質問の答えと比較することで、1回の質問で知ることができる。
クエリ呼び出しはO(NlogN)回、時間計算量はO(N^2logN)となるので、適当な枝狩りをすれば間に合う。

2問目 Robots
実は、最適解において、「弱い」ロボットが運ぶおもちゃを考えると、「Xi>=Xjならば(iが運ぶ任意のおもちゃのW)>=(jが運ぶ任意のおもちゃのW)」が成立することが示せる。この性質を利用する。
解を二分探索することを考えると、「K分以下で片付けられるか?」という問題を解けばよくなる。
まず、「弱い」ロボットにおもちゃを割り当てる。このときロボットiは、「重さXi未満の残っているおもちゃの中で、大きさの大きい方からK個以下」を取るのが最適となる。ロボットをX、おもちゃをWでソートし、尺取り法とpriority_queueを用いることでO(TlogT)でどのおもちゃが取られるかが分かる。
その後は、「小さい」ロボットに、普通に大きさの小さい順に割り当て、最終的に全てを割り当てられたかを判定すればよい。これもO(TlogT)でできる。
時間の最大値がTであることから、O(T(logT)^2)で解けた。

3問目 Games
GCDはセグ木に乗るので、66点までは普通の二次元セグ木を実装すれば取れる。
80点は二次元動的セグ木、100点はそれのメモリ削減が必要らしい。
動的セグ木は、ポインタベースで「ノードが必要になったら作る」ということをすれば書ける。
これを応用すれば二次元にできる。(実装する気が起きない...)

解いた問題

AGC038-C LCMs

https://atcoder.jp/contests/agc038/tasks/agc038_c
Aiの最大値をVとする。今、S(k)=(gcd(i,j)==kなる(i,j)に対するijの和)とすると、lcm(i,j)=ij/gcd(i,j)より、1<=k<=VにおけるS(k)/kの和が答えとなることが分かる。
T(k)=(i,jがともにkの倍数となるような(i,j)に対するijの和)とする。T(k)=(iがkの倍数となるようなiの和)^2 となるので、調和級数より、O(NlogN)で求められる。
T(k)からS(k)を求めるには、余計に数えている分を引く必要がある。実は、kを上から見ていったとき、S(k)=T(k)-(kより大きいkの倍数lにおけるS(l)の和)となるので、これも調和級数からO(NlogN)で求められ、この問題が解けた。
数えなくていい部分も数えているのでAiの和を引いて1/2をすると答えになる。

ABC140-F Many Slimes

https://atcoder.jp/contests/abc140/tasks/abc140_f
Sを大きい順にソートしておく。明らかに、S1が最初のスライムとなる。
時系列順、同じ中では親の大きさ順にスライムを決めていく。
新たにできるスライムの大きさを考えると、「親のスライムより小さいSの中で最大の大きさ」とするのが最適。
もし最大でないものを取ったとして、その後で使われる最大のものとこれをswapしても損しないからである。
これを愚直に実装し、もし途中で構築できなくなったらNo。

CODE FESTIVAL 2016 qual B-E Lexicographical disorder

https://atcoder.jp/contests/code-festival-2016-qualb/tasks/codefestival_2016_qualB_e
SiがSjより辞書順で大きいとき、
1.SjはSiのprefixである
2.最初から見て、2つが初めて相異なるような場所で、Sjの方がSiより辞書順で前にある
のどちらかである。
Trie上で考える。
1.の場合はTrieで前計算することができる。
2.の場合について考える。
「f(i,j,k):Siと他のある文字列Tとの辞書順が決まる場所において、Siの文字がj、Tの文字がkとなるようなTの個数」というテーブルfを前計算しておく。
これはTrie上で、26*O(sum(|S|))程度の計算回数で求めることができる。
各クエリにおいては「Skiより辞書順で小さい文字列の個数」を求めればよい。1.の場合は明らかで、2.の場合は、新しい辞書順でSiの方が後にくるようなfの値のみを足せばよい。
クエリ回答も26^2*O(Q)程度の計算回数で求まるので、制限時間6secに間に合う。

感想

台風のせいで土曜にまともに競プロできなかった、悲しい

9/29-10/5

この場所を有効活用したいと思ったのでばちゃの結果を書くことにした。代表になれないのにIOIばちゃをする人になった(大学生になったらどうせ解かないし...)。
できる日は5時間ばちゃをやってここに記録するようにしたい。

出たコンテスト

CF#589(Div.2) 3完 2095->2017
AGC039 2完 2027->2043

IOI2013 Day1 virtual

100+12+55=167点。謎問題で点数がだいぶ左右されるのつらそう。

~0:20くらい 1問目、2問目を読む。2問目が謎なので後に回すことにして、1問目はすぐに小課題3まで分かる。
~0:40くらい 1問目の小課題4が分かり、それに連動して満点解法も分かる。自分がこの時間で分かるということは簡単枠だろうと思ったので実装することにする。
1:07 1問目の小課題4だけ通る。なぜか一緒に取ろうとした小課題1が通らない。
1:51 謎だったので満点解法を実装すると、通らない理由がわかる。なぜか65点。
1:54 M=N-1で壊れていることに気づく。100点。
2:41 3問目を読むと、小課題3まで分かったので書くと通る。
2:54 すぐに小課題4もわかったので通す。それ以降はヤバい臭いがするので飛ばす。
3:57 2問目がよくつかめなかったので雰囲気で書いて出す。6点。
4:16 微妙な改善を投げるが12点。そのままコンテスト終了。

1問目 Dreaming

各連結成分について、「そこからの連結成分内への距離の最大値が最小となる頂点」のみ他の連結成分と繋げればよい。これに気づくと、次の3つの問題を解けばよくなる。
・木の中で最も遠い2頂点間の距離を求めよ。
・木の中で、「ある頂点を根にしたときの深さの最大値」の最小値を求めよ。
・頂点ごとにコストviが定まっているN頂点0辺のグラフがある。これにN-1辺を追加して、d(i,j)=vi+vj+(iとjの距離)*L の最大値を最小化せよ。
1つ目と2つ目は、全方位木DPをすることで求まる。
3つ目は、実はviが最大の頂点から他の全頂点へ辺を張るのが最適となる。
これは、viが最大な頂点をmとして、m-t1,t1-t2に辺が張られているとき、m-t1,m-t2 に辺を張り直しても損をしないことからわかる。
これでO(NlogN)で解けた。

2問目 Art Class

画像分類をしてくださいという問題。
無思考で「差の二乗和」とかを求めて特徴量を探そうとしていた。
なにもわからない...

3問目 Wombats

55点までは省略。ここから非自明パート(解説を見た)。
最短路テーブルをセグ木に載せることを考える。具体的には、列iから列jまでを被覆するノードでは、「C(p,q)=(i,p)から(j,q)への最短路の長さ」を任意のp,qについて求めたテーブルCを持つことにする。
「D(i,j)=i行目からj行目への、任意の列の2組の最短路テーブル」を求めたいとする。進行方向の性質から、これはi < k<=jなるkについて、D(i,k-1)とD(k,j)から求めることができる。愚直に計算するとO(C^3)。
k-1側の出る場所を固定すると、実は、k側で入る場所を右に動かすにつれ、「k-1からkへ移動する列」も右に動いていくことがわかる。この性質を使えばO(C^2)で求められる。
k=(i+j)/2とすることでセグ木のマージが同じようにできるので、辺更新1回についてO(C^2logR)でできる。
回答クエリについては、全体を被覆する区間を見るだけでよい。O(1)でできる。
これで解けた...と思ったが、実はサイズR*C^2のテーブルを用意しただけでMLEする。時間には余裕があるのでセグ木を工夫するとできるらしいが、面倒になって実装していない...。

解いた問題

JSC2019決勝-A Equal Weight

https://atcoder.jp/contests/jsc2019-final-open/tasks/jsc2019_final_a
15分くらい。300ではない。
まず、A,Bが相異なるので、条件を満たす組で同じシャリやネタが2回使われることはない。
握りの重さの種類数は高々2*10^6通りであることに注目する。
愚直全探索を考えると、シャリとネタの組を全部試し、重さに被りが出たところで終了、というようになり、これはO(NM)に見える。しかし、2*10^6通りより多い組を試した時点で1つ以上被りが生まれるはずである。そのため、2*10^6+1通りより多い通り数が試されることはなく、間に合う。

CF1228E Another Filling the Grid

https://codeforces.com/contest/1228/problem/E
コンテストが後5分長ければ通っていた...。
1の置き方を決めていくことを考えると、「行の中で最初に1が現れる列」が単調増加すると仮定して、
dp(i,j,k):i行目まで見て、i行目の中で最初に現れるのがj列目で、jがk回連続しているときの通り数
というDPをすると、累積和を使って面倒だがO(N^3)で解ける。コンテスト中はこの方法でやった。
上のDPは1の順序を固定してやったせいで面倒になっているが、冷静に考えると「1が既に入っている列の個数」さえ求まっていればよいことがわかる。
dp(i,j):i行目まで見て、1がj個の列に入っている時の通り数
というDPでO(N^3)で解ける。
また、実はこれは包除原理が使える。「i個の行、j個の列に1が含まれていないことがわかっている時の通り数」を計算し、偶奇に応じて足し引きすればよい。O(N^2)で解ける。「または」のみの式は「かつ」の通り数を足し引きすることで求められる、というのが包除原理。

JSC2019決勝-C Maximize Minimum

https://atcoder.jp/contests/jsc2019-final/tasks/jsc2019_final_c
座標xにあるイチゴについて、min(x,L-x)に置いてあるとして考える。
間に2つ以上イチゴのある2つの距離が答えになることはない。また、L/2に最も近い2つについて、(最も近いものを折り返した時の2つの距離)より距離が長くなることはない。よってこれと「間に1つイチゴがあるような2つの距離の最小値」とのminが答えの下界となる。
これは、「左から偶数番目にあるやつを折り返す」というように決めると達成できる。
L/2に近い2つの方の距離は毎回計算してよい。間に1つイチゴがあるような2つの距離は、この2つの集合が一度の操作で定数個しか変化しないことを用いると求められる。
「今の座標の集合」と「間に1つイチゴがあるような2つの距離の集合」をmultisetで管理すればO(NlogN)で解ける。
実装がとてもつらい。

AGC039-B Graph Partition

https://atcoder.jp/contests/agc039/tasks/agc039_b
V1に入れる頂点を1つ固定し、全頂点への最短距離を計算する。すると、最短距離がdの頂点をV(d+1)に入れるのが最適となる。なぜなら、Vi(i>d+1)に入れると壊れるのは明らかで、Vi(i<=d)に入れたとしても、それが条件を満たすならばV(d+1)に条件を満たしたまま移動できるからである。これは最短距離の性質を考えるとわかる。
このようにして構築したグラフのうち、条件を満たすものがなければ-1、あればd+1の最大値が答えとなる。

感想

相変わらず競技プログラミングをする時間がない。
PGBATTLEで入賞していてうれしかった。

9/22-9/28

ブログっぽいことをしようと思いました。

出たコンテスト

CF#558(Div.1) 2完 2018->2095
PG BATTLE 結果不明
ABC142 6完 1982->2027

解いた問題

手応えのあった問題だけ。

JOI2009春 1-2 Stamps

https://www.ioi-jp.org/camp/2009/2009-sp-tasks/2009-sp_tr-day1_20.pdf
30分くらい。
新しい文字列T="IO"+S+"OI"としても、端の"IO""OI"を変化させることに意味はないので、Tのもとで考える。
Tは I..I とO..O が交互に繰り返される形になっている。これらのブロックをまるまる削除したり追加したりすると、2つまとめて消すことになり、必ず損をすることになる。
これでI..I、O..Oを独立に考えてよくなったので、操作回数が最小かつその中で文字列が最短になるものを考えると、
ブロックの長さLが奇数のとき floor(L/2)回変更する
Lが偶数のとき 1文字削ってfloor(L/2)回変更する
のが最適となる。
これを愚直に実装すればO(|S|)で解ける。

CF1229A Marcin and Training Camp

https://codeforces.com/contest/1229/problem/A
30分くらい。
「a_uで立っているbitがa_vで立ってない、またはa_u=a_v」の場合に(u,v)に有向辺を張ったグラフを考える。
明らかに、入次数=0な頂点は使えない。また、それを消すことによって入次数=0になる頂点は使えない。
これを再帰的に繰り返していき、残った頂点は全て入次数>0であり、これらを全て選ぶのが最適。
bit演算を使えば高速にグラフを構築でき、O(n^2)で解けた。

CF1229B Kamil and Making a Stream

https://codeforces.com/contest/1229/problem/B
コンテスト中はこの解法が間に合うとは全く思ってなかった。解説AC。
ある列Aについて、gcd(A_0),gcd(A_0,A_1),...,gcd(A_0,A_1,...,A_N)の中での種類数がO(logA_0)であるということを使う。これは、順に見ていった時にgcdが減る場面では必ず1/2以下になるということから分かる(gcdで掛けられている素因数が1つ以上減るので)
葉側の頂点vをDFSしながら走査していく。unordered_mapを使って「根からvまでのパスの中の、vを含むgcdの集合」を種類ごとに管理できるので、O(nlog(xmax))で解けた。

CF1229C Konrad and Company Evaluation

https://codeforces.com/contest/1229/problem/C
40分くらい。
稼ぐ額が大きい方から小さい方へ辺を張った有向グラフとして見ると、頂点が更新されたときに、入ってきている頂点を全て出ていくように変更し、差分を計算する、という処理をすればよいとわかる。
実は、これを愚直に実装するだけでACできる。
クエリを√M個のブロックに分割して考える。
・頂点がブロックで初めて更新されたとき 
これで更新される辺数の和が2M(次数の和)以下であることは明らか。
・頂点がブロックで2度目以降に更新されたとき 
各頂点について、1度更新されると入次数は0になり、1つのクエリについて高々1つしか入次数は増えないので、ブロック内のクエリ数から、変更する必要のある辺数は√M回以下。よって合計でM回以下。
これを合わせて、O(Q√M)になる。

ABC142-F Pure

https://atcoder.jp/contests/abc142/tasks/abc142_f
「全頂点が入次数1、出次数1のグラフ」はサイクルの集合となる。このうち1つ構築すればよいので、1つのサイクルになるような誘導部分グラフを求めればよい。
まず、元のグラフにサイクルがなければ明らかに-1である。そうでなければ必ず以下のように構築できる。
サイクルの始点を決め、順に見ていく。後ろから入ってくる辺があれば自分の前にある頂点、「入ってくる元の頂点」の後にある頂点は使わないようにする。後ろへ出ていく辺があればその間にある頂点はグラフに含めないようにする。この結果できるグラフは必ず2頂点以上のサイクルになるので、条件を満たす。

感想

コンテスト以外で1問しか解いていなくてまずい。こういうのを可視化していきたい。