AtCoder Beginner Contest 451 F,G問題メモ

F - Make Bipartite 3

問題文

  • 頂点に $1$ から $N$ までの番号が付いた、$N$ 頂点 $0$ 辺の無向グラフ $G$ があります。
  • $Q$ 個のクエリが与えられます。$i$ 番目のクエリでは、グラフ $G$ に頂点 $u_i$ と頂点 $v_i$ を結ぶ辺を追加します。
  • それぞれのクエリを処理した直後のグラフ $G$ において、以下の条件を満たすように $G$ の各頂点を白または黒のどちらか一色で塗ることができるか判定し、可能な場合は黒に塗る頂点の個数としてありうる最小値を求めてください。
    • どの辺についても、両端の頂点が異なる色で塗られている。

制約

  • $2 \le N \le 2 \times 10^5$
  • $1 \le Q \le 2 \times 10^5$
  • $1 \le u_i \lt v_i \le N$
  • $(u_i, v_i) \ne (u_j, v_j) \ (i \ne j)$
  • 入力される値は全て整数

解法

重み付き Union-Find を使ったけど、よく考えると普通の Union-Find でも set で状態管理できるのか。。。

黒を最小化したいので、はじめ、全ての頂点は白で塗られているとする。
クエリによって発生することは以下である。

  • 同じ連結成分にある、違う色で塗られている頂点同士が結ばれる。
    • 何の影響もなく追加できる。
  • 同じ連結成分にある、同じ色で塗られている頂点同士が結ばれる。
    • そこで不可能となり、以降の答えは全て -1 となる。
  • 違う連結成分にある、違う色で塗られている頂点同士が結ばれる。
    • 連結成分はマージされるが、色については影響しない。
  • 違う連結成分にある、同じ色で塗られている頂点同士が結ばれる。
    • どちらか一方の連結成分の色を全て反転させた上でマージする必要がある。(黒が少なくなる方)

難しいのは、通常の Union-Find では「同じ連結成分にあるか」は分かっても、 それが「同じ色で塗られているか」は分からない点。

そこで、重み付き Union-Find を使えば、同じ連結成分内にある相対的な値の差を把握できるので、 各リーダーの色さえ管理しておけば任意の頂点の色を $O(\alpha(N))$ で取得できる。

さらに各リーダーに、現在の自身の連結成分にある白と黒の個数を持たせておけば、マージ時に適切な方を反転させられる。

Python3

G - Minimum XOR Walk

問題文

  • $N$ 頂点 $M$ 辺からなる単純連結無向グラフが与えられます。
  • 頂点には $1$ から $N$ までの、辺には $1$ から $M$ までの番号がそれぞれ付けられており、辺 $i$ は頂点 $U_i$ と頂点 $V_i$ を結ぶ重み $W_i$ の無向辺です。
  • $2$ 頂点を結ぶウォークの重みを、そのウォークが含む辺の重みの総 XOR とします。
    • ただし同じ辺を複数回通る場合、その辺の重みは通った回数分総 XOR に寄与するものとします。
  • 非負整数 $K$ が与えられます。整数の組 $(x,y)$ $(1\leq x\lt y\leq N)$ であって、頂点 $x,y$ を結ぶウォークの重みの最小値が $K$ 以下であるものの個数を求めてください。
  • $T$ 個のテストケースが与えられるので、それぞれについて答えを求めてください。

制約

  • $1 \leq T \leq 10^5$
  • $2 \leq N \leq 2 \times 10^5$
  • $N-1 \leq M \leq 2 \times 10^5$
  • $0 \leq K \lt 2^{30}$
  • $1 \leq U_i \lt V_i \leq N$
  • $0 \leq W_i \lt 2^{30}$
  • 与えられるグラフは単純かつ連結
  • 入力はすべて整数
  • $1$ つの入力に含まれるテストケースについて、$N$ の総和は $2 \times 10^5$ 以下
  • $1$ つの入力に含まれるテストケースについて、$M$ の総和は $2 \times 10^5$ 以下

解法

$O(N^2)$ すら無理な制約でそんなことができるの? と思ってしまう。
でもできるんだねえ。複数のステップを経る面白い問題。

サイクル基底を求めた後、XOR基底を求め、分割統治する。

実現可能なウォークの重み

ウォークの重みは、偶数回通った辺は打ち消し合うので要は「奇数回通った辺の集合」の重みのXORとなる。

「適当に取った全域木での $i→j$ のパス」に 「サイクル基底内のサイクルからいくつか選んで辺をXOR」して作れるものが、 「$i→j$ のウォークで奇数回通る辺の集合」としてあり得るものとなる。

なので、まずグラフのサイクル基底を構築する。

サイクル基底

ここで、「サイクル基底からのサイクルの選び方」自体は $2^{M-N+1}$ 通りあって多すぎるのだが、 「サイクルの重み同士をいくつかXORすることで作れる値」なら、 XOR基底を使うことで $\log_{2}{A_{\max}} = 30$ 個以下の整数に情報を縮約できる。

つまり、「XOR基底の $30$ 個からいくつか選んでXORして作れる値」と、 「サイクル基底からサイクルをいくつか選んで、その重みをXORして作れる値」が、一致する。

まとめると、以下を前計算すればよい。

  • 適当な全域木を構築する
  • 使われなかった辺を全域木に1本ずつ加え、できるサイクルの重みを計算する。
  • その重みから、掃き出し法などでXOR基底を計算する。

ペア数の数え方

頂点 $1$ から各頂点 $v$ に対する、全域木上のパスの重みを求め、これを $d(v)$ とする。

各 $d(v)$ にXOR基底を適用することで「頂点 $1$ から $v$ に行くウォークで、その重みが最も小さくなるもの」が求まる。 これを $e(v)$ とする。

一般に、木において、パスの重みが通過した辺の総XORで計算されるとき、 $i→j$ へのパスの重みは、$d(i) \oplus d(j)$ で求められる。($d(i)$ は頂点 $1→i$ へのパスの重み)

今回の場合も、任意の2点 $u,v$ を結ぶウォークの重みのうち最小のものは $e(u) \oplus e(v)$ で表せる。

略証

「$N$ 個の非負整数 $E=(e(1),...,e(N))$ から、2個選んでXORして、$K$ 以下になる組の個数を求めよ」という問題になった。

上の桁 $d=29,28,...,0$ から考えて、

  • $K$ で $d$ 桁目が立っている場合
    • $E$ を「$d$ 桁目が0のもの」「1のもの」に分類して、同じグループ同士のペアは、XORが $K$ より小さくなることが確定するので、答えに加算する。
    • 異なるグループ同士は、「$E_{zero}$ と $E_{one}$ から1つずつ選んで $K$ より小さくなるペア」を、$d$ を1減らして再帰的に求める。
  • $K$ で $d$ 桁目が立っていない場合
    • $E$ を「$d$ 桁目が0のもの」「1のもの」に分類して、異なるグループから1つずつ選んだペアは、XORが $K$ より大きくなることが確定するので、捨ててよい。
    • 同じグループ同士のペアを、それぞれ再帰的に求める。

こうしていくと、1つの要素は高々 $O(\log{A_{\max}})$ 回しか評価されないため、$O(N \log{A_{\max}})$ でペア数を求めることができる。

Python3

programming_algorithm/contest_history/atcoder/2026/0328_abc451.txt · 最終更新: by ikatakos
CC Attribution 4.0 International
Driven by DokuWiki Recent changes RSS feed Valid CSS Valid XHTML 1.0