・・・(※)を示せば十分です。(y-a)**2+w**2)/sです。ここで、sqrtは平方根、wは川幅、sは泳ぐ速度です。sqrt((y-a)**2+w**2)とし、y'=y-aの関数だと思います。0を用いると、この関数がy'について単調増加であることがわかります。これは(※)の式が成立している事を示します。グラフが与えられている。このグラフの補グラフを連結成分に分解せよ。w∈V)となるグラフのことである。もとのグラフの枝の有無を反転させたグラフのこと。一般の場合n**2≫mなので、補グラフはほぼ完全グラフになってしまっています。Findなりを使って連結成分分解は簡単にできます。今回はn**2が大きいためにこれらの方法をそのままは使えません。何か飛び道具のようなうまい方法がないかと考えたのですが、結局思いつきませんでした。そこで、ベタに連結成分を計算する方法をうまく工夫して軽くできないかを検討しました。最初に考えたのは、データの持ち方の方での工夫でした:まだどの連結成分に属するかが決まっていない節の集合(未決定集合)を持っておきます。深さ優先探索である節から隣接する節に移動する時には、その節が未決定集合に入っているかを調べ、すでに未決定集合から除かれている場合には移動しない事で枝狩りします。Errorになってしまいました(後述のNG解の1つ目がそれです)。setをiteratorで回しながらstd::set::erase(iteraotr)でその要素を削除しているのでiteratorが無効になっています。節の数がn個ある完全グラフを深さ優先探索すると、枝の持ち方を工夫しないと計算にO(n**2)だけかかってしまい、これがこの問題のネックになっています。そこで次の方法として、節を予めまとめあげて節の数を減らしてから、深さ優先探索ができないかを考えました。具体的には、二段階に分かれます。第一段階では、節達のうち距離1でつながっているもの達をある程度つなげることでクラスタにまとめ上げ、第二段階でクラスタに対して(ナイーブな)連結成分分解を行います。どちらの段階も最悪O(n**2)だけかかるのですが、今回の(補)グラフが密である事を用いると第一段階では相当な枝狩りが期待でき、第二段階ではクラスタ数が少なくなることが期待できます。実際うまく制限時間に収まりました(解1)。無効なiteratorを用いないようにしながらtraverseするEditorialを読んでみると、どうやら節同士をまとめてクラスタにするような事をしなくても、NG解1で考えていた、未決定集合を保持しながら幅優先探索で解答が得られるようです。setから単純に値を削除してしまうとiteratorが無効になってしまうので、実装の方法を少し工夫が必要でした。その工夫方法がこちらなどで紹介されていました。この方法を用いたのが(解2)です。グラフが与えられている(節:n個、枝:m本)グラフのうちk個の節に印がついているスタート地点とゴール地点が与えられている次の条件を満たす正数qのうち、最小の値を求めよ。条件を満たすqがない場合には-1と答えよ。条件:「スタートからゴール地点への経路のうち、次の条件を満たすものが存在する。スタート地点からqステップ以内に印つきの節がある、さらにその印つきの節からqステップ以内に印つきの節がある...これを繰り返してゴール地点までたどり着ける。方針1:印つき節のみのグラフを作れるか?もし印つき節だけ取り出し、節間の距離を元のグラフでの最短距離とするグラフを作れれば、その上でダイクストラに近いことを行えば、与えられた課題を解くことができそうです(各節の上で、通常の「その節までの最短距離」の代わりに「その節に到達するまでにたどった枝のコストの最大値」を覚えます)。すると問題はそのような最短距離を結んだグラフが作成可能に帰着できます。嬉しいのは2つの印つき節間の距離がすべて求まる事ですが、それにはワーシャルフロイドを行う必要がありますが、印つき節の最大数は10**5なので、計算量的に無理で、仮にできたとしてもメモリが足らなさそうです。印つき節間の距離を全部は計算しないで計算が間に合うようにするということも考えられるけれど、必要不必要の判断をどのように行うかがパッとは思いつかないです。この方針ではこの先に思いつきませんでした。次に考えたアイデアはqの値について2分探索が出来る事です。ある値qでスタートからゴールまでたどり着くことが可能なら、qより大きい値でもたどり着くことは可能なので2分探索が使えます。qが3なり4なりある特定の定数になってしまえば、今いる印つきの節からqステップ以内にある印つきの点を深さ優先的に探索することで、ゴールに辿り着けるか調べられます。「ある印つき節移動する→その点を始点とするダイクストラを実行し、qステップ以内の印つき節を見つける→それらを順番に訪問する→・・・」とすれば良いです。ダイクストラは無駄な計算をものすごくしているような気がする。印つき節の深さ優先探索でO(k)なので、合計の計算量は大雑把にO(k(m+nlogn))。qについて2分探索の方はそれなりに良さそうな気がしていて、印つきの2つの節について「枝が貼られている」⇔「qステップで移動可能」でグラフを作れば、求める条件は新しく作ったグラフの上でスタート地点とゴール地点が連結であるというところまで問題を簡単にできます。連結かどうかを調べるのは、深さ優先探索でもUnionFindでもできるので、この部分はあまり気にする必要がなさそうです。この場合も方針1の時と同じように、すべての印つき節のペアについてqステップで移動可能かを考えていたら時間は足らなくなってしまう。解答ここまでで良くわからなくなりギブアップ。他の方の回答を見ていたのですが、以下の方針がしっくり来ました。以下のSubmissionを参考にさせていただきました。「qステップで移動可能か」という条件のチェックがナイーブな方法では全く時間が足らないですが、この部分を工夫して計算量を減らすのがポイントだと思います。方針は次のとおりです。各節(印つきとはかぎらない)で「最も近い印つき節」と「その印つき節までの距離」の2つを覚えておきます。qならばv.nとw.nをつなげる」となります。v.d)を入れて、順番にこの規則を適用します。v.d)の情報に更新があったら、その更新情報をキューに追加します。最初に挙げた自分の提出コードに対して、次のテストケースを与えた場合を考えます。このテストケースはT字型をしたグラフでTの先端3箇所にvolunteerがいます。T字の下端が27番の節です。26-42間の距離は30です。答えとなるq=45のケースでは、T字を遠回りしなければゴールにつけません。気になるのはq=55の時で。この場合スタートからゴールまで直接移動できるのですが、自分の提出アルゴリズムでは、やはりT字を遠回りする道を選択しています。印つき節に対し「枝が貼られている」⇔「qステップで移動可能」となっているグラフよりも少ない枝を張っている事で計算量が抑えられているのだと思います。各節に対して覚えておくのは最も近い印つき節だけで覚える事でそれを達成しています。不安になるのは、節のつなぎ漏れがないかということです。実を言うと自分でも何故これで正しくできるかをきちんとは理解できていないですのですが、今回の提出コードではそれで十分だったようです。|カウンター387115。