Toyota Programming Contest 2023#3(AtCoder Beginner Contest 306)G問題メモ

Toyota Programming Contest 2023#3(AtCoder Beginner Contest 306)

ジャッジサーバくんにはヨーグルトと納豆を食べて頑張ってほしい。

G - Return to 1

問題

  • $N$ 頂点 $M$ 辺の有向グラフが与えられる
  • 頂点1から出発して、ちょうど $10^{10^{100}}$ 回の移動後に頂点1にいることが可能か、判定せよ
  • $2 \le N \le 2 \times 10^5$
  • $1 \le M \le 2 \times 10^5$

解法

変なDijkstra。

移動回数が変な値だが、重要な要素は「2と5のみを素因数に含む」こと、 かつめちゃ大きいので「2と5のみを素因数に含むような移動回数で1→1に移動さえできれば、 それを繰り返せばいいだろう」ということ。

(たとえば、移動回数が100回みたいに少ないと、80回(素因数が2と5のみ)で1→1に移動できるルートがあったとしても、 それを繰り返してちょうど100回にすることはできないが、今回はそういうのは心配しなくていいだろう、ということ)

2と5のみを素因数に含む数を「良い数」とする。

1から出発して1に戻ってくるような経路で、長さが良い数のものがあればそれでよし。
無くても、ある2つの経路のGCDが良い数なら、拡張ユークリッド互除法のようにして良い数の周回を作り出せる。

問題は、無限にある経路のGCDをどうやって調べるか、ということ。

差分のGCD

以降、頂点1に戻れないとややこしいので、グラフを「1から到達でき、1に戻れる頂点」のみにしておくとする。

以下の性質を利用する。

  • 集合 $A=(a_1,a_2,...)$ の総GCDは、「$a_1$」と「$(a_1-a_i)$ の総GCD」のGCDと一致する

よって、各頂点に付き、差分で見る。
$a_1$ に相当するものを「最も短い 1→1経路の距離」とし、そこからの差分で考える。

まず、いったん最短経路探索で、各頂点への1からの最短距離 $d_i$ を求めておく。

$d_v=8$ である頂点 $v$ に、別ルートで距離68と98でもたどり着けるとする(差分60と90)。
残りを最短ルートで頂点1まで戻ることを考えると、1→1ルートにおいてもその差分はそのまま残る。
よって、何周かすることで $GCD(60,90)=30$ の調整が可能であることがわかる。
この場合、$v$ から次の頂点へは、距離を $d_v+GCD+1=39$ として探索を進めてよい。

つぎ、$v$ に距離128(差分120)でたどり着いたとする。
既に30単位での調整が可能であることはわかっており、$GCD(30,120)=30$ で更新はない。
その場合、この差分で $v$ 以降を探索しても、既に分かっている差分しか発生しないので次に探索を進めなくてよい。

つぎ、$v$ に83(差分75)でたどり着いたとする。
$GCD(30,75)=15$ となり、GCDに更新が発生する。
その場合、次の頂点への距離を $d_v+GCD+1=24$ として探索を続ける。

このようにして、いずれかの頂点でGCDが良い数になればOK。

ならない場合、同じ頂点が何度も探索されるのでTLEが気になる。
ただ、GCDはどんどん減っていく。
良い数でないGCDだと3の累乗が最も更新回数が多くなるが、 頂点数が上限なので、$3^{11}=177147$ で、11回程度しか更新は発生しない。
(しかも別ルートで $3^{10},3^9,...$ を作らないといけないし、 全ての頂点についてそれを成り立たせることも難しそうなので、実際はもっと少なく済むはず)

Dijkstraの $O((N+M) \log{N})$ に11程度の定数倍が付いても、十分通る。

Python3

programming_algorithm/contest_history/atcoder/2023/0617_abc306.txt · 最終更新: 2023/06/19 by ikatakos
CC Attribution 4.0 International
Driven by DokuWiki Recent changes RSS feed Valid CSS Valid XHTML 1.0