WCPC
AtCoder

ABC469参加記

2026年8月20日 S ShoboiNamae

前回の参加記(ABC468回)

はじめに

こんにちは。ShoboiNamaeです。

最近は甲子園をずっと見ています。智辯和歌山頑張れ。

結果サマリ

問題参加者名タイム言語
Ansubaru0:25Java
Bnsubaru2:38Java
CShoboiNamae10:49C++
DShoboiNamae73:32C++
E---
F---
G---

今週の出題

A問題

問題文を表示

問題文
NN 両編成の電車があります。
この電車の前から KK 両目の車両は後ろから何両目ですか?

制約

  • 1≤K≤N≤1001 \leq K \leq N \leq 100
  • 入力される値はすべて整数

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

出力
答えを 11 行で出力せよ。

N−K+1N - K + 1 を出力する問題でした。

この式を導く方法はいろいろありますが、 KK 両目の前には K−1K - 1 両あることと、 KK 両目の後に N−KN - K 両あることから、後ろから見た場合 N−K+1N - K + 1 両目になるというのはどうでしょうか。

類題

B問題

問題文を表示

問題文
o と x からなる長さ NN の文字列 SS が与えられます。
NN 個の椅子が左右一列に並んでおり、左から ii 番目の椅子には、SS の ii 文字目が o ならば人が座っており、x ならば人は座っていません。
以下の条件をすべて満たす椅子は何個あるか求めてください。
人が座っていない
左に椅子が無い、または左の椅子に人が座っていない
右に椅子が無い、または右の椅子に人が座っていない

制約

  • 1≤N≤1001 \leq N \leq 100
  • NN は整数
  • SS は o と x からなる長さ NN の文字列

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

出力
答えを 11 行で出力せよ。

それぞれの椅子に対して条件を満たすか判定する問題でした。

公式解説では番兵が紹介されていました。

番兵というのは端に記号などを追加して処理を簡単にする方法のことです。そのままやってしまうと端にある椅子だけ例外処理をしないといけませんが、 S = "x" + S + "x"; とすることで全ての処理を共通化できます。ただし、これをしてしまうと添え字がずれることに注意が必要です。添え字のループは 11 から NN になります。

C問題

問題文を表示

問題文
o と x からなる長さ NN の文字列 SS が与えられます。
NN 個の袋が一列に並んでいて、各袋にはお菓子が 11 個ずつ入っています。
ii 番目の袋には、SS の ii 文字目が o ならば「当たり」と書かれており、x ならば「はずれ」と書かれています。
k=1,2,…,Nk=1,2,\dots,N について以下の問題を解いてください。

高橋君は列の先頭から kk 個の袋を受け取り、入っているお菓子を食べ、袋は持っておきます。
その後、以下の行動を可能な限り繰り返します。

  • 高橋君が持っている「当たり」と書かれた袋を 11 個捨て、列の先頭の袋を受け取り、中のお菓子を食べてその袋を持っておく。ただし、列にまだ袋が残っていて、かつ「当たり」と書かれた袋を持っている場合にしか行動はできない。

高橋君が食べることのできるお菓子の個数を求めてください。
袋を受け取った場合、その袋は列から取り除かれることに注意してください。

制約

  • 1≤N≤8×1051 \leq N \leq 8 \times 10^5
  • NN は整数
  • SS は o と x からなる長さ NN の文字列

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

出力
答えを合計 NN 行で出力せよ。
ll 行目には、k=lk=l のときの答えを出力せよ。

k=1,2,⋯ ,Nk = 1, 2, \cdots , N について順番に答えを求めます。

まず重要な考察として、 kk が増えると食べられるお菓子の個数は単調に増加します。 k=mk = m のとき食べられるお菓子の個数は k=m−1k = m - 1 のときよりも(少なくとも 11 つ)多いです。これを利用して解きます。

まず、列の何番目まで食べることができたかを記録する right という変数をおきます。初期値は 00 にします。その後、

  1. k に関するループの始まりです。
  2. right = min(right + 1, N) をします。
  3. right == N || S[right - 1] == 'x' ならば right の値を出力して k を進め、 1. に戻ります。
  4. そうでないなら 2. に戻ります。

この方針をそのまま実装したコードがこちらです。(C++)

ちなみに

公式解説はもっとスマートに解いています。

実は、食べられるお菓子はぴったり kk 番目の’x’までです。よって、‘x’ の位置を調べるとこの問題を解くことができます。

具体的には NN 要素の配列を NN で初期化し、 "(k番目の’x’の添え字)+1( k \text{番目の'x'の添え字} ) + 1" を配列の k−1k - 1 番目に入れます。( 配列・文字列の添え字は 00-indexed )最後はその配列を出力するだけです。

聞いたら納得するけど、何を食べたらこんな解法思いつくんですかね? 煮つけ…?

D問題

問題文を表示

問題文
あるゲームには NN 人のプレイヤー 1,2,…,N1,2,\dots,N がいます。
このゲームは 22 人のプレイヤーが 11 対 11 で戦う形式です。
NN 人のプレイヤーによるトーナメント戦が MM 回行われ、mm 回目のトーナメント戦の決勝にはプレイヤー Am,BmA_m,B_m の 22 人が勝ち上がりました。
以下の条件を満たすような 22 整数 x,yx,y の組がいくつあるかを求めてください。
1≤x<y≤N1 \leq x \lt y \leq N
どのトーナメント戦においても、プレイヤー xx とプレイヤー yy の少なくとも一方が決勝に勝ち上がっている

制約

  • 2≤N≤2×1052 \leq N \leq 2 \times 10^5
  • 1≤M≤2×1051 \leq M \leq 2 \times 10^5
  • 1≤Ai<Bi≤N1 \leq A_i \lt B_i \leq N
  • 入力される値はすべて整数

入力
入力は以下の形式で標準入力から与えられる。
NN MM
A1A_1 B1B_1
A2A_2 B2B_2
⋮\vdots
AMA_M BMB_M

出力
答えを 11 行で出力せよ。

公式解説の方がきれいに解けると思いますが、今回は自分のコンテスト中に出した解法の解説をします。

前置き

入力が

5 3
1 2
3 4
1 4

の時を考えてみると、条件を満たす (x,y)(x, y) の組は (1,3),(1,4),(2,4)(1, 3), (1, 4), (2, 4) の 33 組です。

入力が

5 2
1 2
1 3

の時、条件を満たす (x,y)(x, y) の組は (1,2),(1,3),(1,4),(1,5),(2,3)(1, 2), (1, 3), (1, 4), (1, 5), (2, 3) の 55 組です。

入力が

5 2
1 2
1 2

の時、条件を満たす (x,y)(x, y) の組は (1,2),(1,3),(1,4),(1,5),(2,3),(2,4),(2,5)(1, 2), (1, 3), (1, 4), (1, 5), (2, 3), (2, 4), (2, 5) の 77 組です。

というような調子で入力に対する答えを手作業で求めていくと、最も組の総数が多くなるのは Ai,BiA_i, B_i がそれぞれすべて同じ時のような気がしてきました。その時の組の総数は (N−1)+(N−2)=2N−3(N - 1) + (N - 2) = 2N - 3 通りであり、 NN が最大のケースでも 4×105−34 \times 10^5 - 3 程度です。

組数の上限がその程度であれば、条件を満たす組をすべてsetに入れたり、queueに入れたりしてもメモリ超過しないです。また、私の解法で行った、毎回候補を全部queueに入れて更新する方法が、ワイルドカードを使って工夫することで実行時間内に間に合います。

解法

queueに候補を入れ、毎回全ての候補を見て候補を更新していきます。簡単のため、queueに入れる候補となる組(pair)は first ≤\le second にします。

そして、 Ai,BiA_i, B_i より十分に大きい数をワイルドカード(どの数にもなることを表す)定数WILDとして宣言しておきます。大きくするのはWILDをできるだけ組のsecond側にするためです。また、候補を入れるqueueは事前に (( WILD ,, WILD )) を入れておきます。

Ai,BiA_i, B_i の添え字 ii をfor文で回し、次の手順で処理を行います。

  1. 候補のqueueの要素数を変数szに入れます。
  2. sz回のループを始めます。
    1. まず、候補のqueueの先頭の組のfirstをf、secondをsという変数に代入します。その後、先頭はpopします。
    2. f == WILDならば候補のqueueに (Ai,(A_i, WILD ),(Bi,), (B_i, WILD ),(Ai,Bi)), (A_i, B_i) を入れます(初回の処理)。
      • f == WILDのとき、f ≤\le sよりs == WILDでもあります。つまり、候補 (( f ,, s )) はどちらの要素もワイルドカードなので、ここから出る候補は上記の3つになります。
    3. s == WILDのとき、f == AiA_i または f == BiB_i ならば、候補のqueueに (( f ,, WILD )) を入れます(ワイルドカードの継続)。そうでないなら (( f ,Ai),(, A_i), ( f ,Bi), B_i) を入れます(ワイルドカードの終了)。
    4. fもsもワイルドカードではないとき、f,sに AiA_i, BiB_i と同じ値があれば候補のqueueに (( f ,, s )) を追加します(候補継続)。
      • f,sに AiA_i, BiB_i と同じ値がなかった時は候補のqueueに何も追加されないので、候補は棄却されたことになります。

最後に、queueに入っている候補すべてをsetに入れて重複を除きます。ここでWILDを復元します。具体的には (( f ,, WILD )) がある場合に N−1N - 1 個の組 (( f ,i), i ) をsetに入れます。( ii は 11 以上 NN 以下かつfではない整数)

これで全ての条件を満たす組がsetに入れられたので、答えはsetの要素数になります。

計算量

最初の (( WILD ,, WILD )) からは (Ai,(A_i, WILD ),(Bi,), (B_i, WILD ),(Ai,Bi)), (A_i, B_i) の3つが候補となり、 (( f ,, WILD )) からは (( f ,Ai),(, A_i), ( f ,Bi), B_i) の 22 つが候補になります。また、 (( WILD ,, WILD ),(), ( f ,, WILD )) 以外からは最大で 11 つの候補しか出ないので候補数が増えることはないので、候補のqueueの要素数は最大で 55 個です。

よって、添え字 ii について MM 回ループを回しても十分高速です。

最後のsetに入れる処理も、入れる要素数が最大で 2N−32N - 3 個であることから O(Nlog⁡N)O(N \log N) の計算量で済みます。

E問題

問題文を表示

問題文
o と x からなる長さ NN の文字列 SS が与えられます。
ただし、SS には o が KK 個以上含まれることが保証されます。
高橋君はあるゲームを NN 回行いました。
ii 回目のゲームでは、SS の ii 文字目が o ならば高橋君は勝利し、x ならば高橋君は敗北しました。
高橋君は以下の条件を満たすような 22 整数 l,rl,r を一つ選びます。
1≤l≤r≤N1 \leq l \leq r \leq N
ll 回目から rr 回目までのゲームで KK 勝以上している
このとき、ll 回目から rr 回目までのゲームでの勝率としてあり得る値の最大値を求めてください。

制約

  • 1≤K≤N≤1061 \leq K \leq N \leq 10^6
  • NN と KK は整数
  • SS は o と x からなる長さ NN の文字列
  • SS は o を KK 個以上含む

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

出力
答えを 11 行で出力せよ。
真の答えとの絶対誤差または相対誤差が 10−610^{-6} 以下であれば正解として扱われる。

この回のABCの終了後に界隈では食塩水典型だーという声が上がっていました。

全然知らなかったのですが、確かに応用が効きそうな考え方だなと思いました。

食塩水典型とは?

確かなことは分かりませんが、濃度などを答えで二分探索するテクニックで解くときに、「濃度が pp 以上」という式の変形を使って問題を解く典型だと思います。

今回のE問題の記事を書くにあたって、名前のもとになったと思われる先にABC034 D - 食塩水を解いてみました。(解けなかったので解説を読みました)

食塩水はどういう問題だったかというと、

(ネタバレ防止)

ii 番目の食塩水の食塩が sis_i グラム、 溶液全体が wiw_i グラムとします。

  1. 条件式を変形します。濃度を p%p\% 以上にできるか?という条件式は、 p/100≤(∑iKsi)/(∑iKwi)p/100\le(\sum^K_i s_i)/(\sum^K_i w_i) となるわけですが、ここで、次のように変形できます: 0≤∑iK(si−wip/100)0\le\sum^K_i(s_i-w_ip/100)
  2. 配列を用意し、全ての si,wis_i, w_i について si−wip/100s_i-w_ip/100 が計算できるので、計算して配列に入れてソートします。
  3. ソート結果で値が大きいものから KK 個とり、総和が 00 以上かどうかを判定します。これが二分探索の判定部分で、計算量は二分探索の反復回数を XX として O(XNlog⁡N)O(XN \log N) です。よって、十分高速なのでこの問題を解くことができました。

食塩水を理解したのでこれで今回の問題を解ける!と思ったのですが結局解けず、公式解説を読みました。(食塩水の問題を解いてからの方が開設の内容がよく分かったので効果はあった)

今回の問題の食塩水と違うところ

今回の問題と食塩水の明らかに違うところは、連続する区間を選ぶ必要があるというところです。

食塩水の問題は連続する区間を選ぶ必要がなかったのでソートが有効でした。しかし、今回はそれが使えません。

ところが、連続する区間を選ぶことで使えるようになることもあります。それは累積和です。ということで、今回は累積和を使います。

解法

※ここからは公式解説と同じ解法を書いています。

食塩水の問題と同じように si−wips_i-w_ip を使います。今回の sis_i は S[i] == ‘o’ならば 11 、そうでないなら 00 です。また、 wiw_i は常に 11 です。

今回は連続する区間を選ぶので ∑i(si−p)\sum_i(s_i-p) の計算に累積和が使えます。つまり、区間 ii までの累積和を PiP_i 、選んだ区間を [r,l][r, l] とすると、 ∑i(si−p)=Pr−Pl−1\sum_i(s_i-p)=P_r-P_{l-1} が成り立ちます。このとき、 00 以上を達成するためには PrP_r が大きく、 Pl−1P_{l-1} が小さくなっていた方が嬉しいことは明らかです。

これらのことを使うと、次のようにしてこの問題を解くことができます。

  1. pp を決めます。
  2. si−ps_i-pの累積和をとります。
  3. si−ps_i-pの累積和の累積minをとります。
  4. 尺取り法を用いて各 rr に対する最大の ll を調べます。この ll は、区間 [l,r][l,r] で KK 勝以上する ll です。
  5. rr に関するループを回して rr を全探索します。そして、前の手順で調べた rr に対応する ll における累積minを MM とし、Pr−MP_r-M が 00 以上かどうかを調べます。

以上の 55 つの手順が二分探索の判定部分です。この処理の計算量は O(N)O(N) なので、二分探索の反復回数を XX として、全体の計算量は O(NX)O(NX) になります。二分探索の反復回数は十分少ないと考えられるので、実行制限時間内に解くことができます。

F問題以降

割愛します。

最後に

記事を書くのがかなり遅れてしまい申し訳ございません。記事を頼まれてから書き終わるまでの期間にVSCodeのUIがアップデートで変わってしまいました…(びっくりした)

遅れた分を取り戻そうと思い、問題の振り返りに解法を書きまくることで記事のボリュームをマシマシにしました。A問題を書いていた時はやる気があったので類題までついてます。ただ、遅れた分いい記事を書こうとしてさらに遅れた気も…

ここでひとつ、最後まで読んでくれた方に豆知識を授けましょう。

和歌山大学には「わだにゃん」というかわいいマスコットキャラクターがいます。みかんをかぶっている猫のようなキャラクターです。しかし、愛媛県にも「みきゃん」というかなり似たキャラクターがいます。結構似ています。

でも知ってましたか? みきゃんって犬なんですよ。

わだにゃんは猫、みきゃんは犬ということで、両者棲み分けができているのですね。

最後までお読みいただきありがとうございました。

← 活動記事一覧に戻る