AtCoder Regular Contest 141 E問題メモ
最近のARC、青・黄くらいの難易度帯が少ない。
E - Sliding Edge on Torus
問題
- $N \times N$ の正方形状に頂点が並んだグラフを考える
- 平面座標のように $(0,0)~(N-1,N-1)$ で頂点番号を表す
- グラフには、はじめ、辺はない
- $Q$ 個のクエリが順に与えられるので、以下のように辺を追加する
- クエリは $a,b,c,d$ で与えられる
- $k=0~N-1$ について、$((a+k) \mod N,(b+k) \mod N)$ と $((c+k) \mod N,(d+k) \mod N)$ 間に辺を追加する
- 各クエリ後における、グラフ全体の連結成分数をそれぞれ答えよ
- $2 \le N \le 2 \times 10^5$
- $1 \le Q \le 2 \times 10^5$
解法
頂点とクエリの変形
1つのクエリで辺が追加される頂点のうち、 $((a+k) \mod N,(b+k) \mod N)$ の頂点をまとめて $(a,b)$ 側の頂点、もう一方を $(c,d)$ 側の頂点と呼ぶことにする。
追加する辺が斜めにずれていくのがややこしいので、頂点の並べ方を、↘が↓となるよう以下のように変形する。
(0,0) (0,1) (0,2) (0,3) (1,1) (1,2) (1,3) (1,0) (2,2) (2,3) (2,0) (2,1) (3,3) (3,0) (3,1) (3,2)
こうすると、$(a,b)$ 側、$(c,d)$ 側がそれぞれ同じ列になるので、ちょっと考えやすくなる。
また、クエリ $(a,b),(c,d)$ を上端にシフトして、$a=0$ にして考える。こうしてもクエリに対する操作に変化はない。
クエリを $a=0$ にシフトした上で、改めて $b,d$ を 「変形後のグラフ上で左から何列目か」という情報に置き換えた値を $(a',b'),(c',d')$ とする。
たとえば、$(a,b),(c,d) = (1,3),(2,3)$ の場合、シフトして $(0,2),(1,2)$ となり、 さらに列を置き換えて $(a',b'),(c',d')=(0,2),(1,1)$ となる。
- $a'=0$
- $b'=(b-a)\mod{N}$
- $c'=(c-a)\mod{N}$
- $d'=(d-a-c')\mod{N}$
以降、「列」などの表現は、変形した後の並びにおけるものを表すとする。
列ごとの連結成分数
全ての頂点を表現することはメモリ的にできないので、列ごとの連結成分数を記録しておく。
はじめは、全列 $N$ であり、連結成分数は $N^2$ である。
初期状態に「同列に対するクエリ」「異なる列に対するクエリ」が来たときにどうなるか考える。
同列に対するクエリ
必ずしも列の全ての頂点が連結になるとは限らない。
たとえば $N=6$ で $c'=3$ だった場合、3個おきにグループができる。(連結成分は3個となる)
(0,0)┐ 連結成分数 6→3 (1,1)┼┐ (2,2)┼┼┐ (3,3)┘││ (4,4)─┘│ (5,5)──┘
一般に、その列の現在の連結成分数が $p$ だった場合、新たな連結成分数は $GCD(p,c')$ となる。
全体の連結成分数 $ans$ は、$ans-p+GCD(p,c')$ に更新される。
異なる列に対するクエリ
列は統合される。
いま、考察の対象とするのは、あくまで双方の列が、まだ他の列と統合されたことがない場合に限るとする。
一般に、統合される前の列の連結成分数が $p,q$ だった場合、新たな連結成分数は $GCD(p,q)$ となる。
全体の連結成分数 $ans$ は、$ans-p-q+GCD(p,q)$ に更新される。
2 3 ←クエリ前の連結成分数 ┌(0,0)─(0,1)┐ ┌┼(1,1)─(1,2)┼┐ クエリ後はGCD(2,3)=1 のため、 │├(2,2)─(2,3)┼┼┐ 連結成分数は1(全体が連結)になる ├┼(3,3)─(3,4)┘││ │└(4,4)─(4,5)─┘│ └─(5,5)─(5,0)──┘
Union-Findなどで統合列を管理するとよさそう。
クエリにより統合された複数の列を、「列グループ」と呼ぶこととする。
異なる列に対する複数のクエリ
統合されたことのない列同士なら上記のような感じで良いのだが、実際は、統合済みの列がさらに統合されたり、同じ2列の異なる頂点にクエリが来たりする。
この処理をどうするかが難しい。
Union-Findで $b',d'$ が同じ列グループだったとする。
その時、列グループの現在の連結成分数が $p$ なら、クエリ後は $GCD(p,c')$ になる?かというと、そうでもない。
【A】 【B】 \ \ (0,0) `(0,1) (0,0)\ (0,1) \ \ \ (1,1) (1,2) (1,1)\ (1,2) \ \ \ (2,2) (2,3) (2,2)\ (2,3) \ \ \ (3,3) (3,4) (3,3)\ (3,4) \ \ \ (4,4) (4,5) (4,4)\ (4,5) \ \ \ (5,5) (5,0) (5,5)\ (5,0) ` `
この両者、いずれも連結成分数は6だが、ここに (0,0)→(3,4) の辺を新たに張ると、$GCD(6,3)=3$ にはならず、 連結成分数は【A】は2、【B】は1になる。
この両者を区別するような、データの持たせ方をしないといけない。
ここで、「異なる列グループの間に最初に張る辺は、必ず平行な辺とするよう、一方をシフトさせる」ことを考える。
すると上記はそれぞれ以下のようになり、
【A】 【B】 (0,0)─(1,2) (0,0)─(2,3) (1,1)─(2,3) (1,1)─(3,4) (2,2)─(3,4) (2,2)─(4,5) (3,3)─(4,5) (3,3)─(5,0) (4,4)─(5,0) (4,4)─(0,1) (5,5)─(0,1) (5,5)─(1,2)
(0,0)→(3,4) の辺は、それぞれ
- 【A】は(0,0)と(3,4)=2段下を結ぶ。$GCD(6,2)=2$
- 【B】は(0,0)と(3,4)=1段下を結ぶ。$GCD(6,1)=1$
となり、結果の連結成分数と一致する。
要は、$c'$ そのものではなく、「$c'$ と、過去に張られた辺との差分」とのGCDが必要で、
シフトすることでそれを図的にイメージしやすくなる。
あれ、GCDをとるのは最初に張られた辺との差分だけでいいの? 3本目が追加されたとき、2番目と3番目の差分は考慮できてなくない? と思うが、 $g=GCD(t_2-t_1,t_3-t_1)$ とすると、$t_1=a_1g+k,t_2=a_2g+k,t_3=a_3g+k$ のように表せるので、結局、$t_3-t_2$ も $g$ の倍数であり、変化しないので大丈夫。
統合済みの列同士の統合
さて、$N=6$ とし、「列1を基準にすると、列2は5のシフト」「列3を基準にすると、列4は3のシフト」という統合済みの列グループが2つあったとする。
列2・4の間に、「列2を基準にすると、列4は2のシフト」が必要な新たなクエリが飛んできたとする。
この場合、全体を統合した結果は「列1を基準にすると、列2は5、列3は4、列4は1のシフト」となる。($N$ でループする点に注意)
統合済みの1つの列グループに対し、一律にシフト値を増減させても関係性は変わらないので、 新たなクエリに矛盾しないように調整する必要がある。
しかし、列グループのサイズは $O(N)$ なので、毎回、一方の列グループの全ての値を書き換えていたら、$O(NQ)$ かかる。
ここで、重み付きUnion-Find、または、ポテンシャル付きUnion-Find などと呼ばれるデータ構造が役に立つ。
要は、以下のような特徴を持ったデータ構造である。
- 各頂点に、整数値 $A_i$ を割り当てていく(この問題ではシフト値に相当)
- いくつかの $(x,y)$ について、「$A_y-A_x=d$ である」という条件が決まっている
- 条件を順に適用していき、任意のタイミングで、各 $A_i$ を、その時点までの条件に矛盾しないような状態に保つことができる
- たとえば、$(w,z)$ を指定して、$A_z-A_w$ が一意に決まるか判定し、決まるならその値を得ることができる
これを使って、クエリで指定された列 $b',d'$ が、、、
- 未統合なら
- $b'$ と $d'$ の現在のシフト値 $S_b,S_d$ を得る
- $d'$ の側の列グループ全体をさらに $S_b+c'-S_d$ だけシフトさせつつ、2つの列グループを統合する
- 両者の統合前の連結成分数のGCDが、統合後の列グループの連結成分数
- 統合済みなら
- $b'$ と $d'$ の現在のシフト値 $S_b,S_d$ を得る
- $c'-(S_d-S_b)$ が、シフトを考慮した上での「何段下と結ぶか」を示す値となる
- 自身の列グループの連結成分数を、上の値とのGCDに更新する
としていくとできる。
もちろん、ポテンシャルUnion-Findを使わなくても、クエリを2回走査して、1回目で全体に矛盾しないシフト値を求め、2回目でそれを使って実際に答えを求める、などでもよい。