WCPC
AtCoder

ABC477参加記

2026年9月26日 N nsubaru

前回の参加記(ABC476回)

はじめに

こんにちは。nsubaruです。 夏休み中だったこともあり、この記事も止まっていたのですが、もう終わったので2週間遅れでこの記事を書いています。

今回も最終的には勝っているので良いですが、水色になってからはE問題まで解かないと下がるっていうプレッシャーで、毎コンテストドキドキですね。 だからといって Unrated で出るのは甘えなのでもちろん Rated で参加しました。

結果サマリ

問題参加者名タイム言語
Ansubaru0:47Java
BShoboiNamae6:31C++
CShoboiNamae13:37C++
DShoboiNamae38:32C++
Ensubaru57:13Java
F---
G---

今週の出題

A問題

問題文を表示

問題文

ある信号機の色は B(青)、Y(黄)、R(赤)、B(青)、Y(黄)、R(赤)…\dots という順に、周期的に変化します。

現在の信号機の色を表す文字 cc が与えられます。次の信号機の色を出力してください。

制約

  • cc は B、Y、R のいずれか

入力

入力は以下の形式で標準入力から与えられる。

cc

出力

次の信号機の色を表す文字を出力せよ。

if 文を3つ書けば解ける問題ですね。タイピングがそこまで速くないので、クォーテーション書かないといけない問題系は嫌いです(´;ω;`)

B問題

問題文を表示

問題文

数直線上に NN 人の人がいます。人には 11 から NN までの番号がついており、人 ii は座標 XiX_i にいます。ただし、同じ座標に複数の人がいる場合もあります。

ある人が「一線を画す」とは、自分以外のどの人との距離も DD 以上であることをいいます。

厳密には、人 ii が「一線を画す」とは、 すべての j (1≤j≤N, j≠i)j ~ (1 \leq j \leq N,\ j \neq i) に対して ∣Xi−Xj∣≥D|X_i-X_j| \geq D が成り立つことをいいます。

「一線を画す」人の番号をすべて昇順に出力してください。

制約

  • 2≤N≤1002 \le N \le 100
  • 1≤D≤1091 \le D \le 10^9
  • 1≤Xi≤1091 \le X_i \le 10^9
  • 入力される値は全て整数

入力

入力は以下の形式で標準入力から与えられる。

NN DD
X1X_1 X2X_2 …\dots XNX_N

出力

一線を画す人の人数を KK、その人たちの番号を昇順に並べた列を (P1,P2,…,PK)(P_1,P_2,\dots,P_K) とする。以下の形式で出力せよ。

KK
P1P_1 P2P_2 …\dots PKP_K

ただし K=0K=0 の場合、22 行目は空行とせよ。

毎度のことながら B 問題って問題文を見落としがちで、人数を出力するだけかと思ったり、未満を判定しようとして以下になっていたり、安定の 1WA でした。 最近実際に自分の提出を目視調査してみたのですが、B問題のWA率は高めでしたね。問題に関しては、それぞれの人について全探索し、条件を満たす人を List なり Vector なりに加えていって最後にそのサイズと中の要素を出力すればよいですね。

C問題

問題文を表示

問題文

文字列 S,TS, T が与えられます。 QQ 個のクエリを処理してください。 クエリでは L,RL, R が与えられるので、SS の LL 文字目から RR 文字目までからなる部分文字列に、TT が部分文字列として含まれるかを判定してください。

部分文字列とは
SS の部分文字列とは、SS の先頭から 00 文字以上、末尾から 00 文字以上削除して得られる文字列のことをいいます。 例えば、ab は abc の部分文字列ですが、ac は abc の部分文字列ではありません。

制約

  • SS は長さ 11 以上 4×1054 \times 10^5 以下の英小文字からなる文字列
  • TT は長さ 11 以上 1010 以下の英小文字からなる文字列
  • 1≤Q≤2×1051 \leq Q \leq 2 \times 10^5
  • 1≤L≤R≤∣S∣1 \leq L \leq R \leq |S|
  • Q,L,RQ, L, R は整数

入力

入力は以下の形式で標準入力から与えられる。

QQ
SS
TT
query1\mathrm{query}_1
query2\mathrm{query}_2
⋮\vdots
queryQ\mathrm{query}_Q

各クエリ queryq\mathrm{query}_q は以下の形式で与えられる。

LL RR

出力

QQ 行出力せよ。qq 行目には qq 番目のクエリについて、SS の LL 文字目から RR 文字目までからなる部分文字列に、TT が部分文字列として含まれれば Yes を、含まれなければ No を出力せよ。

文字列 SS の指定された部分文字列に文字列 TT が含まれるか判定する問題です。

部分文字列として含まれるかだけを判定するのなら簡単なのですが、この問題は指定された区間内に含まれるかを判定しないといけないため少し難易度は上がります。

用いた解法は SS 内の ii 番目の位置から文字列 TT が作れるかどうかを前計算で判定しておき、ii 番目から文字列 TT が作れる場合は 1 、そうでなければ 0 となるような配列を作成します。 あとは任意区間について 1 となるような値が 1 つでも存在すれば TT が存在することを示せるので、その判定を行うために累積和を取り任意区間の総和が 1 以上かどうかという判定を行うことで解きました。

D問題

問題文を表示

問題文

NN 個のマスが横一列に並んでおり、左から順にマス 11 からマス NN までの番号がついています。色は英小文字で表されます。

はじめ、すべてのマスの色は a で、各マスには何も置かれていません。

QQ 個のクエリが与えられるので、与えられた順に処理してください。各クエリは以下の 22 種類のいずれかです。

  • 1 X : マス XX にタイルが置かれていなければタイルを置き、置かれていれば取り除く。

  • 2 C : タイルが置かれていないすべてのマスの色を CC に変更する。

すべてのクエリを処理した後の各マスの色を(タイルが置かれているかどうかにかかわらず)求めてください。

制約

  • 1≤N,Q≤3×1051 \le N,Q \le 3\times 10^5
  • 各クエリはタイプ 11 またはタイプ 22 のいずれか
  • タイプ 11 のクエリにおいて、XX は 1≤X≤N1 \le X \le N を満たす整数
  • タイプ 22 のクエリにおいて、CC は英小文字 11 文字

入力

入力は以下の形式で標準入力から与えられる。

NN QQ
query1\mathrm{query}_1
⋮\vdots
queryQ\mathrm{query}_Q

各クエリ queryi (1≤i≤Q)\mathrm{query}_i ~ (1 \le i \le Q) は

11 XX

または、

22 CC

の形で与えられる。

出力

最終的な各マスの色を、左から順につなげた長さ NN の文字列として出力せよ。

最近大体のD問題がよくある問題だなと思ってきたのですが、これもまあ典型ですね。と言いつつ、ぱっと見分からず先にE問題解いたのですが…

クエリタイプ 1 の処理は O(1)\mathcal{O}(1) 、2 の処理は O(N)\mathcal{O}(N) です。そのため極端な場合、2 の処理しかなければ O(NQ)\mathcal{O}(NQ) になり間に合いません。 この問題で求めなければいけないものはクエリを処理した後の最終的なマスの色です。このことからクエリの途中に各マスがどんな色になっているかは分からなくてもよい、つまり各マスごとに最後に塗られた色のみを覚えておけばよいと分かります。

これはクエリを逆順に見ていくというテクニックを用いることで解けます。逆順に見ていくと各マスが何色になるかが確定していくので、まだ色が確定していないマスの集合を保持しながら色を確定させていけば解くことができます。

この問題ちょっと意地悪で、Java で HashSet を使って提出したのですが、TLE になり C++ に書き換える必要がありました。この好プレーで一応 AC することが出来ました。 コンテスト後なんでダメだったのかなと考えていたのですが、この問題は集合に対してループする処理を行う必要があり、集合が保持している要素数に対して実際の内部配列のサイズは多くなっているため、そこで重くなったのかなと。 結果的に Java のコードでも TreeSet や他のデータ構造だと通ったため、HashSet に対する拡張 for 文は十分に気を付ける必要があるなと思いました。良い学びです。

E問題

問題文を表示

問題文

頂点に 11 から N+1N+1 の番号がついた N+1N+1 頂点の辺重み付き無向グラフが与えられます。 1≤i≤N1 \leq i \leq N を満たす整数 ii について、頂点 ii と頂点 (i mod N)+1(i \bmod N) + 1 を結ぶ重み AiA_i の辺があります。 また、1≤i≤N1 \leq i \leq N を満たす整数 ii について、頂点 ii と頂点 N+1N+1 を結ぶ重み BiB_i の辺があります。これ以外の辺はありません。

QQ 個のクエリを処理してください。クエリでは S,TS, T が与えられるので、頂点 SS から頂点 TT への最短経路の長さを求めてください。

制約

  • 3≤N≤2×1053 \leq N \leq 2 \times 10^5
  • 1≤Q≤2×1051 \leq Q \leq 2 \times 10^5
  • 1≤Ai,Bi≤1091 \leq A_i, B_i \leq 10^9
  • 1≤S<T≤N+11 \leq S \lt T \leq N+1
  • 入力される値は全て整数

入力

入力は以下の形式で標準入力から与えられる。

NN QQ
A1A_1 A2A_2 …\dots ANA_N
B1B_1 B2B_2 …\dots BNB_N
query1\mathrm{query}_1
query2\mathrm{query}_2
⋮\vdots
queryQ\mathrm{query}_Q

各クエリ queryq\mathrm{query}_q は以下の形式で与えられる。

SS TT

出力

QQ 行出力せよ。qq 行目には qq 番目のクエリの答えを出力せよ。

はい。 D よりも先に解いた問題です。問題を見て解法がすぐに思いつかなかったのですが、ダイクストラを使いたいなという気持ちを持ちながらボードにお絵かきして考えていました。 E 問題は典型のアルゴリズムやテクニックをどう適用するかみたいな問題が多いなという印象で、今回もダイクストラを使いたいという気持ちが答えとなる発想に繋がったと思います。

こういった難しい問題に挑むにあたって大事なことは簡単な問題条件に変えて、絶対にわかることから潰していくことです。早期判定をいっぱい作ることで、解くべき問題を明確にすることができ自然と解法を導くことができます。

まず頂点 N+1N+1 が無い場合を考えます。この場合は左回りか右回りの 2 通りになり、どちらの方がコストが低いのかを考えるだけです。 次に頂点 N+1N+1 を絶対に通る場合について、頂点 N+1N+1 を通って他の頂点に行った後、もう一度頂点 N+1N+1 に戻ってくることは明らかに不要です。 したがって、頂点N+1N+1を通る場合はある頂点 UU から何らかのルートで頂点 N+1N+1 に行き、頂点 N+1N+1 からまた別のルートを通って頂点 VV に行くというルートになります。

この時自分が考えたのは、他の頂点から始まっても頂点 N+1N+1 から次の頂点への最短経路を求める必要があるから、ここをダイクストラしておけばとりあえず半分は消せるなーと。 …ああ逆から見ても同じか!?となって、ある頂点から頂点 N+1N+1 に行くというルートも頂点 N+1N+1 を始点とするダイクストラを最初にしておけば、UU -> N+1N+1 間も N+1N+1 -> VV 間も O(1)\mathcal{O}(1) で求められるということが分かりました。 後は頂点 N+1N+1 を通る場合とそうでない場合(左回りか右回りか)の比較を行うだけで解くことができます。

F問題

問題文を表示

問題文

NN 行 MM 列のグリッドが与えられます。上から ii 行目では、左から LiL_i 列目から RiR_i 列目までのマスが黒く塗られており、それ以外のマスは白く塗られています。

QQ 個のクエリが与えられます。各クエリでは、以下の問題に答えてください。

  • 整数 A,B,C,DA,B,C,D が与えられます。上から AA 行目から BB 行目まで、左から CC 列目から DD 列目までの長方形領域に含まれる黒いマスの個数を求めてください。

制約

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

入力

入力は以下の形式で標準入力から与えられる。

NN MM QQ
L1L_1 R1R_1
L2L_2 R2R_2
⋮\vdots
LNL_N RNR_N
query1\mathrm{query}_1
⋮\vdots
queryQ\mathrm{query}_Q

各クエリ queryi (1≤i≤Q)\mathrm{query}_i ~ (1 \le i \le Q) は

AA BB CC DD

の形で与えられる。

出力

QQ 行出力せよ。ii 行目には、ii 番目のクエリに対する答えを出力せよ。

ここからはコンテスト中には全く分からなかったのですが、コンテスト後に解説ACすることが出来たので書いていこうと思います。 本当はコンテストから時間も経ったのでこの記事自体書くつもりがなかったのですが、この問題は初めての解法だったので書く価値があるなと思い書いています。

何から説明すべきか難しいのですが、問題を整理するとまず NN 行 MM 列のグリッドとあり、これは NM≥1010NM \ge 10^{10} であるためデータとして保持することはできないなと考えます。 ではどうやってグリッドの情報を表現し、クエリを処理していけばよいかというと、まずあるクエリ qq に着目し AqA_q 行目から BqB_q 行目までの行について、CqC_q 列目から DqD_q 列目までをループして探索するのではなく、1 列目から DqD_q 列目までの黒マスの総数から1 列目から Cq−1C_q-1 列目までの黒マスの総数を引くということを考えます。

こうすることで O(N)\mathcal{O}(N) 必要だった処理が、O(1)\mathcal{O}(1) になります。これはグリッドの情報を行ごとに 1 列目からの累積情報として持っていれば可能です。 この 1 列目からの累積情報というのが重要で、現在処理している列を xx とし、ある行 ii について 1 列目から xx 列目までの黒マスの総数の求め方を以下の3通りの条件に分けて整理することでどのように持てばよいかが見えてくると思います。

  1. x<Lix < L_i の場合は 00
  2. Li≤x≤RiL_i \le x \le R_i の場合は、x−Lix - L_i
  3. Ri<xR_i < x の場合は、Ri−LiR_i - L_i

これを列ごとに全ての行を更新することなく管理する方法を考えます。ここで出てきている値について、00, LiL_i, RiR_i は定数であることから、一度 xx を忘れて定数部分の遷移だけみると、各列ごとに毎回値を変える必要はなく初期値を 0 として LiL_i, RiR_i の場所で 2 回この定数を変えるだけでよさそうです。 またこの定数部分は各行ごとに求める必要があり、さらにその任意区間の総和(行AA ~ BB までの区間和)が欲しいのですが、これは セグメント木 を使うことで簡単に求めることができます。

状態2については、この状態になっている行だけ xx を加算させることを考えます。これも同様に セグメント木 を用いることで xx を加算するべき状態 2 の個数として求めることができます。

最後に列ごとに処理を行いたいので、LL, RR, CC, DD を全てソートして 1 列に並べたときに適切にイベント(現在見ている値がクエリの左端なのか右端なのか、黒マスの開始か終了か)を処理すれば解くことができます。

G問題

問題文を表示

問題文

頂点に 11 から NN の番号がついた NN 頂点の木が与えられます。ii 番目の辺は頂点 uiu_i と頂点 viv_i を結んでいます。また、頂点 ii には整数 xix_i が書かれています。 QQ 個のクエリを処理してください。クエリでは整数 s,t,a,bs,t,a,b が与えられるので、以下の条件を満たす整数 yy の個数を求めてください。

  • 頂点 ss と頂点 tt を結ぶパス上にある頂点のうち、整数 yy が書かれた頂点の個数を ff とする。この時、a≤f≤ba \leq f \leq b が成り立つ。

制約

  • 2≤N≤2×1052 \leq N \leq 2 \times 10^5
  • 1≤Q≤2×1051 \leq Q \leq 2 \times 10^5
  • 1≤ui<vi≤N1 \leq u_i \lt v_i \leq N
  • 入力で与えられるグラフは木
  • 1≤xi≤N1 \leq x_i \leq N
  • 1≤s<t≤N1 \leq s \lt t \leq N
  • 1≤a≤b≤N1 \leq a \leq b \leq N
  • 入力される値は全て整数

入力

入力は以下の形式で標準入力から与えられる。

NN QQ
x1x_1 x2x_2 …\dots xNx_N
u1u_1 v1v_1
u2u_2 v2v_2
⋮\vdots
uN−1u_{N - 1} vN−1v_{N - 1}
query1\mathrm{query}_1
query2\mathrm{query}_2
⋮\vdots
queryQ\mathrm{query}_Q

各クエリ queryq\mathrm{query}_q は以下の形式で与えられる。

ss tt aa bb

出力

QQ 行出力せよ。qq 行目には qq 番目のクエリの答えを出力せよ。

前提知識として、Mo’s Algorithm が必要で、そのうち木上のMoとかいうアルゴリズムを使えば簡単に解くことができる問題です。 ライブラリ化してしまえば誰でも解けるため、もっとdiffが低くてもおかしくないのではと思うのですが、そもそもレアなアルゴリズムで使いどころも見極めづらいのかなと思います。

木上のMoについての話をすると、まず使える前提としてクエリの問題でありそのクエリはそれぞれ独立していて、並び替えが可能であり、木上の任意の2頂点を結ぶパスに対してのクエリであるということです。 そしてそのパスに頂点が追加されたり、削除されたりした際の状態の変化が O(1)\mathcal{O}(1) やそれに近いオーダーの場合に使用できます。

必要な知識としては、Euler Tour, LCA, Mo’s Algorithm かなと思います。

とりあえず今回ライブラリ化できたので、次は殴れます。

最後に

こういう記事は書くことで思考の過程を整理することができ、その解法に対する再現性が生まれてくるので非常に意味があるなと思っています。僕が担当する回はFとかGとかも積極的に書いていければなと思います。 最後までお読みいただきありがとうございました。nsubaruでした~。

← 活動記事一覧に戻る