UNICORNプログラミングコンテスト2026(AtCoder Beginner Contest 477)D,E,F,G問題メモ

D - Masking Tape

問題文

  • $N$ 個のマスが横一列に並んでおり、左から順にマス $1$ からマス $N$ までの番号がついています。
  • 各マスに色を塗っていきます。色は英小文字で表され、はじめ、すべてのマスの色は a です。また、各マスには何も貼られていません。
  • $Q$ 個のクエリが与えられるので、与えられた順に処理してください。各クエリは以下の $2$ 種類のいずれかです。
    • 1 X : マス $X$ にマスキングテープが貼られていなければ貼り、貼られていれば剥がす。
    • 2 C : マスキングテープが貼られていないすべてのマスの色を $C$ に変更する。
  • すべてのクエリを処理した後の各マスの色を(マスキングテープが貼られているかどうかにかかわらず)求めてください。

制約

  • $1 \le N,Q \le 3\times 10^5$
  • 各クエリはタイプ $1$ またはタイプ $2$ のいずれか
  • タイプ $1$ のクエリにおいて、$X$ は $1 \le X \le N$ を満たす整数
  • タイプ $2$ のクエリにおいて、$C$ は英小文字 $1$ 文字

解法

色を上書きしていくクエリ問題は、逆から処理するとやりやすいことが多い。
重要なのは「最後に何で塗られたか」だけなので、逆順だと「最初に塗られた色」に確定し、 そのマスについての以降のクエリは考慮しなくて済む。

とりあえずクエリ1だけ正順で処理して、最終的な状態においてマスキングテープが貼られていないマスの集合を $S$ とする。
$S$ は、(逆順で)「次にクエリ2が来たらその色で塗られるマス」という意味合いを持ち、既に色が確定したマスは入らないものとする。

クエリを逆順に処理し、

  • クエリ1が来たら、
    • そのマスの色が確定済みなら何もしない。
    • 未確定なら、$S$ に入ってなければ入れ、入ってたら除く。
  • クエリ2が来たら、
    • $S$ にあるマスの色を全て $C$ で確定させる。$S$ を空にする。

とし、最後まで未確定のマスは初期値の a で塗られていたことにすると、$O(N+Q)$ で処理できる。

Python3

E - Wheel Distance

問題文

  • 頂点に $1$ から $N+1$ の番号がついた $N+1$ 頂点の辺重み付き無向グラフが与えられます。
    • $1 \leq i \leq N$ を満たす整数 $i$ について、頂点 $i$ と頂点 $(i \bmod N) + 1$ を結ぶ重み $A_i$ の辺があります。
    • $1 \leq i \leq N$ を満たす整数 $i$ について、頂点 $i$ と頂点 $N+1$ を結ぶ重み $B_i$ の辺があります。
    • これ以外の辺はありません。
  • $Q$ 個のクエリを処理してください。
    • クエリでは $S, T$ が与えられるので、頂点 $S$ から頂点 $T$ への最短経路の長さを求めてください。

制約

  • $3 \leq N \leq 2 \times 10^5$
  • $1 \leq Q \leq 2 \times 10^5$
  • $1 \leq A_i, B_i \leq 10^9$
  • $1 \leq S \lt T \leq N+1$
  • 入力される値は全て整数

解法

タイトル通り、車輪のようなグラフである。

 ,--①--,
⑥-,|,-②
|  ⑦  |
⑤-'|`-③
 `--④--'

中央の頂点($N+1$)から他の各頂点への最短距離を $d_v$ とし、Dijkstra等で事前計算しておく。

クエリで、$T=N+1$ の場合、$d_{S}$ が答えとなる。

それ以外の場合、グラフ上のパスは「中央を通る」か「外側を時計回り/反時計回りで回る」かのいずれかしかない。

中央を通る場合、$d_S+d_T$ である。
外側を回る場合、$A_i$ の累積和を取っておけば求められる。

この3種類を比較して、最小のものが答えとなる。

Python3

F - Count Cells in a Window

問題文

  • $N$ 行 $M$ 列のグリッドが与えられます。
  • 上から $i$ 行目では、左から $L_i$ 列目から $R_i$ 列目までのマスが黒く塗られており、それ以外のマスは白く塗られています。
  • $Q$ 個のクエリが与えられます。各クエリでは、以下の問題に答えてください。
    • 整数 $A,B,C,D$ が与えられます。
    • 上から $A$ 行目から $B$ 行目まで、左から $C$ 列目から $D$ 列目までの長方形領域に含まれる黒いマスの個数を求めてください。

制約

  • $1 \le N,M,Q \le 2\times10^5$
  • $1 \le L_i \le R_i \le M$
  • 各クエリについて、$1 \le A \le B \le N$
  • 各クエリについて、$1 \le C \le D \le M$
  • 入力される値はすべて整数

解法

クエリ先読みして平面操作。

長さ $M$ の配列 $R$ を、「1行目から、今注目中の行までの、各列の黒マスの個数」として用意する。
1行進む度に「特定の区間に $1$ を足す」ことになるので、$R$ は遅延セグメント木などで実装する。
$i$ 行目時点の $R$ の状態を $R_i$ で表すとする。

              i R
              0 [0 0 0 0 0 0]
□■■■□□  1 [0 1 1 1 0 0]
■□□□□□  2 [1 1 1 1 0 0]
□□□■■■  3 [1 1 1 2 1 1]

すると、クエリは $R_B[C:D]-R_{A-1}[C:D]$ で求められる。 ただし $R_i[C:D]$ は $R_i$ の $C~D$ 列目の和を表すとする。

これを分解して、

  • $i$ を順番に処理し、$R$ を更新していく。
  • $A-1$ 行目を更新したら、$R_{A-1}[C:D]$ を求め、そのクエリの答えから引く。
  • $B$ 行目を更新したら、$R_{B}[C:D]$ を求め、そのクエリの答えに足す。

とすると、各クエリに正しく答えられる。

Python3

G - Frequency Query on Tree

問題文

  • 頂点に $1$ から $N$ の番号がついた $N$ 頂点の木が与えられます。
  • 頂点 $i$ には整数 $x_i$ が書かれています。
  • $Q$ 個のクエリを処理してください。
    • 整数 $s,t,a,b$ が与えられるので、以下の条件を満たす整数 $y$ の個数を求めてください。
      • 頂点 $s$ と頂点 $t$ を結ぶパス上にある頂点のうち、整数 $y$ が書かれた頂点の個数を $f$ とする。この時、$a \leq f \leq b$ が成り立つ。

制約

  • $2 \leq N \leq 2 \times 10^5$
  • $1 \leq Q \leq 2 \times 10^5$
  • $1 \leq u_i \lt v_i \leq N$
  • 入力で与えられるグラフは木
  • $1 \leq x_i \leq N$
  • $1 \leq s \lt t \leq N$
  • $1 \leq a \leq b \leq N$
  • 入力される値は全て整数

解法

なかなか重実装。解法を解説するサイトには辿り着けたが、実装が間に合わず。

仮に木ではなく数列であったとしても、「出現回数」「種類数」といった集計はセグメント木などでは扱いづらい。 代わりに Mo's Algorithm 等を用いると計算可能となる。

木でも Mo's が使えたらいいのに……!

使えちゃうらしい。上のサイトの [木上の Mo] → [パスに対するクエリ] に図付きで解説が載っている。

前計算として、例えば頂点 $1$ を根として以下を求めておく。

  • オイラーツアーでの通過順に頂点を並べたものを $T=(T_1,...,T_{2N-1})$ とする。
  • 各頂点、$T$ 上で初めて出現する index を $F_v$ とする。
  • オイラーツアーで $i$ 番目に通過する辺の両端について、より深い方の頂点を $U=(U_1,...,U_{2N-2})$ とする。

また、$s$ と $t$ の最小共通祖先 $\operatorname{LCA}(s,t)$ も求められるようにしておく。

すると、$s,t$ 間のパス上の頂点についての何らかの集約値は、 「$U$ の $[F_s,F_t)$ の範囲のうち、奇数回登場する頂点」に 「$\operatorname{LCA}(s,t)$」を加えたものの集約値として表すことができる。

$s,t$ 上のパスから分岐する寄り道は、$U$ 上では偶数回登場するので打ち消される、という性質を使っている。

これを元に今回、Mo's で管理しようと思うと、以下の3つが必要となる。

  • $\operatorname{parity}[i]:=$ 頂点 $i$ が現在の区間内に登場する偶奇
  • $\operatorname{freq}[x]:=$ 現在の区間内に存在する、値 $x$ が書かれた頂点の個数
  • $\operatorname{acc}[f]:=$ freq上で $f$ 回以下出現する値の個数

区間を伸ばしたり縮めたりするとき、まずはparityを参照し、 追加/削除しようとしている頂点の寄与を集約値(freq,acc)に加えるのか、除くのか、をまず特定する。

いずれにしても、$f$ は1回の追加/削除操作では $\pm 1$ でしか増減しないため、acc への影響も $O(1)$ で反映できる。
$f$ から $f+1$ に増えたなら $\operatorname{acc}[f]-=1$ となるし、 $f+1$ から $f$ に減ったなら $\operatorname{acc}[f]+=1$ となる。

答えは、その時点の $\operatorname{acc}[b]-\operatorname{acc}[a-1]$ で求められる。

Python3

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