2024-08-15

Codeforces Round 966 (Div. 3)に参加した

競プロアルゴCodeforces

Codeforces Div. 3の参加記.

8月13日に開催されたCodeforces Round 966 (Div. 3)に参加した.結果はABCDEFGの7完で275位.

Div. 3はコンテスト終了後にHackのフェーズが12時間ありその後でようやくシステムテストが始まるので,レート更新を待つ時間が永遠に感じられた.

A - Primary Task

問題概要

問題文

整数a  (1a104)a\;(1\leq a\leq 10^4)が与えられるので,以下の条件を満たす整数x  (x2)x\;(x\geq 2)が存在するかどうか判定せよ.

考えたこと

コーナーケースに注意して丁寧に判定していく.aa1010進表記をa~\tilde aとすると,以下の条件がすべて満たされていればよい.

Rustでの文字列操作はやっぱりまだ慣れない.

提出:https://codeforces.com/contest/2000/submission/276116576

B - Seating in a Bus

問題概要

問題文

nn個の座席1,,n1,\cdots, n11列に並んでいるバスがある.nn人の乗客1,,n1,\cdots, nがこの順にバスに乗り込み,乗客iiが座った座席はaia_iであった.このとき,以下の条件が成り立つか判定せよ.

考えたこと

座席iが埋まっているかを表す配列occupied[i]を管理しつつ,乗客1,,n1,\cdots, nの順に条件が満たされているかどうかを逐次的にチェックしていけばよい.座る席が端っこのとき配列外参照しないように注意.

提出:https://codeforces.com/contest/2000/submission/276133887

C - Numeric String Template

問題概要

問題文

長さnnの整数列(ai)(a_i)と整数mmが与えられる.j=1,,mj=1,\cdots, mに対して,以下のmm個の判定問題を解け.

考えたこと

要するに,整数と英小文字が1対1対応するかを判定すればよい.そのためには,i=1,,ni=1,\cdots, nの順に数列と文字列を同時に走査しながらHashMapなどを用いて「整数→英小文字」と「英小文字→整数」の対応を記録していき,対応先が複数存在する箇所を発見したら答えをNOとする.

提出:https://codeforces.com/contest/2000/submission/276161477

D - Right Left Wrong

問題概要

問題文

長さnnの正整数列(ai)(a_i)と,LまたはRからなる長さnnの文字列ssが与えられる.あなたは以下の操作を00回以上好きな回数だけ行うことができる.

このとき,最終的に獲得することのできる点数の最大値を求めよ.

考えたこと

操作が11回までしか許されないなら簡単で,最左にあるLと再右にあるRを選ぶのが最適である.操作が複数回許される場合でも,できるだけ区間の幅が大きくなってほしいというのは変わらないので,直感的には「まだ選んでいない端点のうち,最左にあるLと再右にあるRを選ぶ」ことを繰り返せばよいように思える.これは,累積和を構築したのちしゃくとり法の亜種のように区間の左端と右端を管理するポインターを左と右から走査させていくことでO(n)O(n)でできる.

さすがに未証明のまま満足するのはよろしくないのでざっくりに正当性を考えておく.最大値を与えるような操作列(l1,r1),,(lm,rm)(l_1,r_1),\cdots, (l_m, r_m)があったとする.まず,操作順は得られる点に影響しないのでl1<<lml_1<\cdots < l_mとしてよい.さらに,li<lj<rj<ril_i < l_j < r_j < r_iとならないようなi<ji < jが存在した場合,端点を組み替えることで点の総和を悪化させずにli,lj,rj,ril_i, l_j, r_j, r_iをこの順に並べることができる.よって最適解の形ははじめからl1<<lm<rm<<r1l_1 < \cdots < l_m < r_m < \cdots < r_1の形だけ考えればよく,その制約下では確かに「まだ選んでいない端点のうち,最左にあるLと再右にあるRを選ぶ」のが最適である.

提出:https://codeforces.com/contest/2000/submission/276184108

E - Photoshoot for Gorillas

問題概要

問題文

ww匹のゴリラがおり,ii番目のゴリラの身長はaia_iである.

n×mn\times mのグリッドと整数kkが与えられたとき,グリッド上の異なるww個のマスを選んでゴリラを配置することを考える.ゴリラiiをマス(xi,yi)(x_i, y_i)に配置したとき,その配置の眺めの良さss(spectacle)を,グリッド上のk×kk\times kの正方形領域に含まれるゴリラの身長の総和をすべての正方形領域について足し上げたもの,と定義する.より厳密には

s=x=1nk+1y=1mk+1ixi[x,x+k)yi[y,y+k)ais=\sum_{x=1}^{n-k+1}\sum_{y=1}^{m-k+1}\sum_{\substack{i\\ x_i\in [x, x+k)\\ y_i\in [y, y+k)}}a_i

である.このときssの最大値を求めよ.

考えたこと

マス(x,y)(x,y)にゴリラiiを配置したとき,上の和にaia_iが何回登場するかを考える.これはマス(x,y)(x,y)を含むようなk×kk\times k領域の個数に一致し,その数は

(min(nk+1,x)max(0,xk)+1)(min(mk+1,y)max(0,yk)+1)(\min(n-k+1, x)-\max(0, x-k)+1)(\min(m-k+1, y)-\max(0, y-k)+1)

O(1)O(1)で求められる(あるいは2次元imos法を使ってO(nm)O(nm)かけてまとめて計算してもよい).

こうして各マス(x,y)(x,y)にゴリラを置いたときの重みが分かったので,あとは身長が高い順に重みの大きいマスに割り当てていけばよい.この貪欲パートの計算量はO(nm(logn+logm)+wlogw)O(nm(\log n+\log m)+w\log w)である.

提出:https://codeforces.com/contest/2000/submission/276214198

F - Color Rows and Columns

面白かった!

問題概要

問題文

nn個の長方形があり,ii番目の長方形はaia_ibib_i列のグリッドである.あなたは以下の操作を何回でも行うことができる.

kk点以上をとるために必要な操作回数の最小値を求めよ.kk点以上を獲得することが不可能な場合は-1を出力せよ.

考えたこと

最適な手筋は単純な構造をしていなさそう.サイズの小さいグリッドから塗っていくのは必ずしも最適とは限らず,複数のグリッドをまんべんなく塗っていくほうが効率がよいこともありそうだ.また,グリッドを選んでもすべてのマスを塗るとは限らず,いくつかの行や列が埋まった状態で終わりにするほうがよい場合もあるだろう.

いろいろ考えるうちに,この問題を次のように言い換えるのがよいことが分かった.

それぞれの長方形に対して「色を塗るか塗らないか」「塗るとしたらどこまで塗るか」が選択できるとき,操作回数の最小値は?

このような枠組みで問題を捉え直すと,これがまさしくDPで解ける構造をしていることが分かる.具体的には,

のようなDPテーブルを定義して逐次的に値を求めていけばよい.

ただし遷移は少し複雑だ.まず長方形iiを塗らない場合に対応して

dp[i][j]min(dp[i][j],dp[i1][j]){\rm dp}[i][j] \leftarrow \min({\rm dp}[i][j], {\rm dp}[i - 1][j])

という更新をする.

問題は長方形iiを塗る場合だ.マスをどこまで塗るかによって得られる点数が異なるため,p=1,,ai+bip=1,\cdots, a_i+b_iのそれぞれに対して,pp点得る場合に要する最小操作回数wpw_pを計算する必要がある(得られる点数はマスを全部塗ったときが最大で,このとき行数と列数の合計であるai+bia_i+b_i点が入る).注意しなければならないのは,「つねに行方向に塗る」「つねに列方向に塗る」などの戦略は必ずしも最適であるとは限らない点である.例えば3×33\times 3のグリッドを塗るとき,つねに一方向に塗るよりも


  xxx  3  ooo  2  ooo  2  ooo  1  ooo  1  ooo
  xxx  →  xxx  →  oxx  →  ooo  →  ooo  →  ooo
  xxx     xxx     oxx     oxx     oox     ooo

のように,交互に塗ったほうが(最終的に獲得する点数は同じでも)早く点数を獲得できる.より一般には,塗る方向を新たに決めるたびに,その行/列を塗るために必要な操作回数(まだ色が塗られていないマスの数)がより少ない方向を貪欲に選んでいくとよい.これをp=1,,ai+bip=1,\cdots, a_i+b_iの順に行っていき,その都度

dp[i][j+p]min(dp[i][j+p],dp[i1][j]+wp){\rm dp}[i][j+p] \leftarrow \min({\rm dp}[i][j+p], {\rm dp}[i - 1][j]+w_p)

という更新を行えばOKである.

このDPの状態数はO(nk)O(nk),遷移は各iiに対してO(ai+bi)O(a_i+b_i)で,制約が1n103,1k102,1ai,bi1021\leq n\leq 10^3, 1\leq k\leq 10^2, 1\leq a_i,b_i\leq 10^2なので実行時間制限に十分間に合う.

提出:https://codeforces.com/contest/2000/submission/276255664

G - Call During the Journey

TL 4 secとRustに甘えてlogを2つ付けました……

問題概要

問題文

nn頂点mm辺の単純無向グラフが与えられる.あなたは頂点11を時刻t  (<t0)t\;(< t_0)に出発し,辺を移動して頂点nnまで移動する予定である.

各辺i=1,,mi=1,\cdots, mには22種類の重みli(1)<li(2)l_{i}^{(1)} < l_{i}^{(2)}が設定されており,それによって辺iiを通る際の所要時間が以下のように決まっている.

また,あなたは任意の頂点で任意の時間だけ立ち止まってもよい.

加えて,時刻t1t_1から時刻t2  (t1<t2<t0)t_2\;(t_1 < t_2 < t_0)には電話の予定があり,この間(端点t1,t2t_1, t_2を含まない)にバスに乗っていてはならない.

このとき,頂点nnへの到着時刻がt0t_0以下であるような出発時刻ttの最大値を求めよ.

考えたこと

問題設定は複雑だが解き方はシンプル.

ひとまずttを固定して考えると,頂点nnへの最も早い到着時刻はダイクストラ法で求めることができる.遷移がやっかいだが,時刻tt'に頂点vvを出て辺iiを移動するとき,考慮すべきなのは

の3つで十分である.

ttを固定したときの判定問題が解けたので,あとはttの境界を二分探索で求めればO((n+m)lognlogt0)O((n+m)\log n\log t_0)でこの問題に答えることができる.

後から気づいたが,頂点nnを起点として逆向きにダイクストラ法を適用すれば二分探索なしで求めることが可能である.

提出:https://codeforces.com/contest/2000/submission/276275564