AtCoder Beginner Contest 460 E,F,G問題メモ

E - x + y ≡ x + y

問題文

  • 正整数 $a,b$ に対して、$a$ と $b$ を繋げて書いた時に出来る整数を $\mathrm{concat}(a, b)$ と呼びます。
    • 例えば $a = 123, b = 45$ の時 $\mathrm{concat}(a, b)=12345$ です。
  • 正整数 $N, M$ が与えられます。
  • $N$ 以下の正整数の組 $(x,y)$ であって $\mathrm{concat}(x, y) \equiv x + y \pmod{M}$ であるものの個数を $998244353$ で割った余りを求めてください。
  • $T$ 個のテストケースが与えられるので、それぞれについて答えを求めてください。

制約

  • $1 \leq T \leq 10^4$
  • $1 \leq N \leq 10^{18}$
  • $2 \leq M \leq 10^9$
  • 入力される値は全て整数

解法

数値を文字列として連結する系の問題は、下の方の桁数を固定し、数式で表すのがセオリー。

$y$ を $d$ 桁とすると、$\mathrm{concat}(x, y) = 10^dx+y$ であり、問題の条件は移項して以下のようになる。

  • $(10^d-1)x \equiv 0 \pmod{M}$

$y$ は式から消え、$N$ 以下の $d$ 桁の正整数でさえあれば何でもいいということになる。つまり $y$ の候補は

  • $d$ が $N$ の桁数と等しいとき、$N-10^{d-1}+1$
  • それ未満の時、$9 \times 10^{d-1}$

一方、$x$ は、$10^d-1$ と掛けて $M$ の倍数になる数、つまり $g=\mathrm{GCD}(10^d-1,M)$ とし、$M/g$ の倍数なら何でも良いので、$\lfloor \frac{N}{M/g} \rfloor$ 個の候補がある。

互いに独立に決めていいので、掛け合わせたものが $d$ 桁の時の答えとなる。

Python3

F - Farthest Pair Query

問題文

  • $N$ 頂点の木があります。はじめ、すべての頂点は黒く塗られています。
  • 以下のクエリを $Q$ 個順に処理したときの各クエリの答えを求めてください。
    • 整数 $x$ $(1 \leq x \leq N)$ が与えられる。
    • 頂点 $x$ が白く塗られているならば黒く、黒く塗られているならば白く塗り替える。
    • その後、黒く塗られている頂点どうしの距離の最大値を求める。
  • なお、クエリを順に処理したとき、操作の過程で黒く塗られている頂点が常に $2$ 個以上存在するような入力のみが与えられます。

制約

  • $3 \leq N \leq 10^5$
  • $1 \leq U_i, V_i \leq N$
  • 与えられるグラフは木
  • $1 \leq Q \leq 10^5$
  • 各クエリについて、$1 \leq x \leq N$
  • どの時点でも黒く塗られている頂点が $2$ 個以上存在する
  • 入力される値はすべて整数

解法

木が固定なら「$\mathrm{DP}[v]:=v$ の部分木内で最も遠い黒頂点までの距離」などとして木DPできるが、 クエリで更新が発生する場合、効率的に情報を保持することが難しい。

1頂点の更新が親や子までのパス全体に影響するので、 HL分解して親子関係を1つのパスで表しセグ木などに載せるのかな、などと考えていたが、 黒↔白のどちらの変化か、変更される頂点は今までの最長パスの片方であったか、などなど 様々なケースを考えていたら、どんどん管理しないといけないデータが増え、手に負えなくなった。

公式Editorialの解法を見ると、 セグ木は使うのだが、木の問題であるにもかかわらず、載せる配列の並び順は木構造ではなく頂点番号順($1,2,...$)というもの。 オイラーツアーとかHL分解の順だろうということにとらわれて思いつけなかったなぁ。

頂点の集合 $\{1,2,...,N\}$ の部分集合 $S$ について、その中に黒頂点が2個以上ある場合、$f(S)$ を以下で定義する。

  • $f(S):=S$ 内の黒頂点間を結ぶパスの中で、最長のもの。(複数ある場合はその1つ)
    • $(a,b)$ 間を結ぶパスを $p(a,b)$ で表す

この時、以下が成り立つ。

  • 2つの集合 $S,T$ で、$f(S)=p(a,b),f(T)=p(c,d)$ だった場合、$f(S \cup T)$ に該当するパスは、$\{a,b,c,d\}$ から2頂点選んだものを結ぶパスの中に必ず存在する。

略証

なので、「高々 $4$ つの新規組候補 $(a,c),(a,d),(b,c),(b,d)$ について2点間距離を調べ、既存の $f(S),f(T)$ を更新するか調べる」ことで、$f(S \cup T)$ を求められる。

これはセグ木に載せることができる。
$[l,...,r)$ を示すノードは、$S=\{l,l+1,...,r-1\}$ とした上で $f(S)$(端点と距離)を載せておけばよい。 $S$ 内に黒頂点が $0~1$ 個しかなく $f(S)$ が定義されない場合、適当に無効であることを示す値で代用する。 (黒が1頂点あるなら、その頂点番号は保持しておく必要がある)

2点間距離は LCA などを前計算しておけば求められる。
今回は、本問のクエリ1回毎に、セグ木内で約 $4 \log{N}$ 回の距離計算クエリが走るため、 前計算に多少かかっても、距離計算クエリを高速にした方が望ましい。 Sparse Table を使うことで距離計算クエリが $O(1)$ となる。

Python3

G - Vertex Flip Query

問題文

  • 頂点に $1$ から $N$ の番号がついた $N$ 頂点の木があります。
  • 頂点 $i$ には重み $W_i$ と色 $C_i \in \lbrace 0,1 \rbrace$ が設定されています。
  • クエリを $Q$ 個処理してください。クエリは以下の $3$ 種類のいずれかです。
    • 1 v:頂点 $v$ について $C_v$ を $1-C_v$ に変更する。
    • 2 v x:頂点 $v$ について $W_v$ を $W_v + x$ に変更する。
    • 3 v :頂点 $v$ の色を $c$ とする。頂点 $v$ から色が $c$ である頂点のみを通って到達可能な頂点(頂点 $v$ 自身も含む)全てについての重みの総和を出力する。

制約

  • $1 \leq N \leq 3 \times 10^5$
  • $1 \leq Q \leq 2 \times 10^5$
  • $1 \leq W_i \leq 10^9$
  • $C_i \in \lbrace 0,1 \rbrace$
  • $1 \leq a_i \lt b_i \leq N$
  • 入力されるグラフは木
  • $1 \leq v \leq N$
  • $1 \leq x \leq 10^9$
  • 入力される値は全て整数

解法

HLD解。かなりの重実装。

まず、Heavy-Light Decomposition で木をパスに分解する。

クエリ3に答えることを想像するとき、

 :
[③○●●●❷●○○○●●]
       |  |
       | [❶●●●○○]
      [●○]   ↑
               クエリ指定頂点 v
  • まず、$v$ と同じ色が列の最初の頂点(❶)まで続き、かつ親(❷)とも同じ色である限り、親の列に遡っていく。
    • 親(❷)を新たな $v$ と扱う。
    • 上例では、1つ遡ったところで、$v$ の列の最初の頂点③とは異なる色となるので、止まる。
  • 遡った列上で、$v$ を含む、$v$ と同じ色が連続した区間(●●●❷●)について、何らかの前計算列の区間和で答えが求まるようにしたい。
    • 各頂点、自身の $W_i$ と、Light-Child の列の先頭から自身と同じ色が続く限りの $W_i$ の合計値が入っていればよい。
[○○❶❷❸❹❺○○○●●]    ❷には❿の Wi が、
       |  |                 ❹には❻❼❽❾⓫⓬ の Wi が
       | [❻❼❽❾○○]      足し込まれていれば、❶❷❸❹❺の和で答えが求まる
      [❿○]     |        
                [⓫⓬○○○]

なので、列に対して以下ができればよい。

  • 色反転を考慮しつつ $v$ と同じ色が連続した区間がどこからどこまでか求める
  • 色反転と加算を考慮しつつ「$V_i:=W_i$ およびHeavy以外の $i$ の子の列で同じ色が続く限りの $W_j$ の合計値」を管理する

1つめは、$C$ において色が切り替わる部分のindexを、SortedSet などで管理しておけば二分探索で求められる。
クエリ1による更新も高々2つのadd,removeで管理できる。

2つめは、黒/白それぞれで「自身の Light-Child の列で、先頭の色が黒/白であるようなものの、先頭から同じ色が続く限りの $V_i$ の合計値」を管理する。

  • $X_i:=i$ の Light-Child で、先頭が黒であるようなものの、先頭から黒が続く限りの $V_i$ の合計値
  • $Y_i:=i$ の Light-Child で、先頭が白であるようなものの、先頭から白が続く限りの $V_i$ の合計値
[○○●●● ❶ ●○○○●●]    ❶に対して
           /|\                     Xi: ❷❸❹❺の各Viの和(❻❼のViは❹に適用済みである点に注意)
          || [①②③●●●]       Yi: ①②③の各Viの和
          |[❷❸❹○○○]
         [❺○○]|             ❶のVi: ❶自身のWi + ❶と同じ色の子の合計値(この例ではXi)
                [❻❼○○]

「列 $a$ の、先頭から同じ色が続く限りの $V_i$ の合計値」を $L_a$ とする。

列 $a$ のどこかを更新する際は、更新前の先頭の色 $C_{a0}^{before}$ と合計 $L_a^{before}$ を保持しておき、 更新後の先頭の色 $C_{a0}^{after}$ と合計 $L_a^{after}$ と比較する。

before と after が変化していたら親にその影響を伝えなければならないが、それは $C,L$ のbefore/afterを元に親の $X_i,Y_i$ を差分更新できる。
その更新によって、親の $V_i$ が変わりうり、親の列 $b$ の $L_b$ も変化しうる。 変更は連鎖的になるが、HLDのため最大 $O(\log{N})$ 回で収まる。
変化がなくなったら伝播を止めていい。

まとめると、HLD後の各パスにつき、$C_i,W_i,X_i,Y_i$ は配列で保持しつつ、

  • 列 $a$ の、$V_i$ に関する一点更新・区間和取得のセグメント木またはFenwich木
  • 列 $a$ の各 $C_i$ の変化点の SortedSet

を用意すれば、管理できる。

  • クエリ1(色変更)
    • $C_i$ をスイッチし、色の変化点のSortedSetも更新する。
    • $W_i,X_i,Y_i$ を元に $V_i$ を更新する。
    • 親の列に変更を伝播する。
  • クエリ2(加算)
    • $W_i,V_i$ に指定値を加算する。
    • 親の列に変更を伝播する。

計算量は $O(N \log{N} + Q (\log{N})^2)$ で重くはあるが、 $(\log{N})^2$ の部分の各 $\log{N}$ は、一方はHLDの列数、もう一方は1列当たりの要素数のlog を表すため、 両方が同時に最大となることはなく、何分の1かの定数倍がかかることで、なんとか間に合う。

Python3

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