Loading [MathJax]/extensions/TeX/mathchoice.js

AtCoder Regular Contest 192 (Div. 2) A,B,C,D,E問題メモ

A - ARC Arc

問題文

  • 正整数 N と、長さ N0 または 1 からなる数列 A=(A1,A2,,AN) が与えられます。
  • 長さ N の英大文字のみからなる文字列 S であって、以下の操作を 0 回以上好きな回数行うことで A0 が含まれないようにできるものを 良い文字列 と呼びます。
  • 次のうちどちらかを選び、実行する。
    • 1iN なる整数 i であって Si= A、Si+1= R、Si+2= C となるものを選び、Ai,Ai+11 に置き換える。
    • 1iN なる整数 i であって Si+2= A、Si+1= R、Si= C となるものを選び、Ai,Ai+11 に置き換える。
  • ここで、Si (1iN)Si 文字目を表し、SN+1=S1,SN+2=S2,AN+1=A1 とします。
  • 良い文字列が存在するか判定してください。

制約

  • 3N200000
  • Ai{0,1} (1iN)
  • 入力はすべて整数

解法

ARCRARCRA… のように“ARC”と“CRA”を重ね合わせつつ繰り返せばほとんどの Ai は“1”に置き換えることができる。

特に、[ARCR…ARCR] のように最後の“A”の手前で切って最初に戻るものは、全て“1”にできる。
このようなことができるのは、N4 の倍数の時である。

4で割ったあまりで場合分けすることを考えると、1か3の場合は、

N%4
  1    ARCR...ARCRA
       1111...11110      ←操作により1にできる位置
  3    ARCR...ARCRARC
       1111...1111110    ←操作により1にできる位置

いずれも最後の位置だけ操作により“1”にすることができない。
逆に言うと、ここが初期状態で“1”だった場合は良い。
indexはループしていて任意の位置に合わせられるため、初期状態に“1”が1つでもあれば良い。

2余る場合が厄介。この場合、2箇所はどうしても“1”にできない。この位置に初期状態で“1”があるなら良い。

N%4
  2    ARCR...ARCRAR
       1111...111100

ただ、2つの“1”は連続している必要は無い。2つの「偶数個の“1”+“0”」に分け、この“0”の両方に初期状態を合わせられればよい。

       ARCR...ARCARCR...ARCRA
       1111...1101111...11110

この時、2つの“0”のindexの偶奇は異なっている。
初期状態で Ai=“1” である i を偶奇に分けた時、いずれにも1つ以上あれば良い。

Python3

B - Fennec VS. Snuke 2

問題文

  • フェネックとすぬけくんがボードゲームで遊んでいます。
  • 正整数 N と、長さ N の正整数列 A=(A1,A2,,AN) が与えられます。また、集合 S があり、はじめ S は空集合です。
  • フェネックとすぬけくんは、フェネックから順に次の一連の操作を交互に行います。
    • 1Ai なる i を選ぶ。Ai から 1 を引き、iS ならば Si を追加する。
    • S={1,2,,N} となっていたら、最後に操作したプレイヤーを勝者としてゲームを終了する。
  • フェネックとすぬけくんは、どちらも自身が勝者になることを目指して最適な行動を行います。
  • どちらが勝者になるか判定してください。

制約

  • 1N2×105
  • 1Ai109 (1iN)
  • 入力はすべて整数

解法

実験したら、証明はともかく、法則性は簡単に推測できる。(また、ゴールが見えた方が証明もしやすい)
ただ、証明は場合分けが多くて大変。

とりあえず小さいケースでは、

  • N=1 の時は明らかに「先手必勝」
  • N=2 の時、S に一方を追加すると相手は即座にもう一方を追加すれば勝つ。「後手必勝」
  • N=3 の時、
    • A に2種類目の要素を追加したら、相手は即座に3種類目を追加するので負ける
    • → なるべく2種類目の要素の追加を相手に押しつけたい
    • → 先手が1種類目に追加した i を、Ai が0になるまで互いに選び続ける
    • Ai=0 となり2種類目を追加せざるを得ないのは、Ai が奇数なら後手、偶数なら先手
    • 先手は奇数の Ai があればそれを1種類目とすることで「先手必勝」。なければ「後手必勝」

N4 以降は分岐が多くなるので人力での全探索は難しい。(実際はここから実験に転換した)
ただし、小さいケースで分かったこともあって、「A で1以上の要素がラスト2になった状態」が重要そうである。

問題を以下のように言い換える。
一旦 S に追加した後の Ai はどれを選んでも変わらないので、X という1変数にまとめてしまえる。

  • 数列 A={A1,...,AN} と、変数 X=0 を用意する。
  • 各手番では2種類の操作のいずれかを選んで行う。
    • A から1要素 Ai を選び、取り除く。Ai1X に加算する。
    • X を1減らす。(X>0 の場合のみ)
  • A を空にする操作をした方が勝ち

A に残る要素が2、かつ X が偶数の状態で相手に手番を回せる方が勝つ。
X は、ひいてはその加算元となる各 Ai は、偶奇のみが重要と分かる。

更に以下のように言い換える。

  • A にある奇数の要素数を Co、偶数の要素数を Ce とする。1bit変数 X=0 を用意する。
  • 各手番では3種類の操作のいずれかを選んで行う。
    • Co を1減らす。(Co1 の時のみ)
    • Ce を1減らし、X を反転させる。(Ce1 の時のみ)
    • X を1減らす。(X=1 の時のみ)
  • Co=Ce=0 にする操作をした方が勝ち

初期状態を以下で場合分けする。ただし Co+Ce4 とする。
3つ組の意味を、(Co,Ce,X) とする。
また、4 のような表記は「4以上の偶数」を意味するとする。

  • (,,0)
  • (,,0)
    • ②a) (0,5,0)
    • ②b) (2,3,0)
    • ②c) (4,,0)
  • (,,0)
  • (,,0)
    • ④a) (1,3,0)
    • ④b) (3,,0)

①は後手必勝である。後手は、先手と同じ操作をすれば先手に (,,0) を渡す状態をキープできる。
この状態はいつか (0,0,0) となり、後手を最後の手番として終了する。

また③は、先手が初手で Co を選べば①を後手に回せるので、先手必勝となる。

②④がややこしい。Co,Ce が少なくなると行動が変わる。

④は先手必勝となる。

  • ④a) (1,3,0) の時、先手は Co を減らし、(0,3,0) を相手に渡す。
  • 後手は Ce を操作するしかなく、(0,2,1) が返ってくるので、(0,2,0) を返せば①に帰着して先手必勝。
  • ④b) (3,,0) の時、先手は Ce を減らし、(3,,1) を相手に渡す。
  • 後手は、
    • Co を減らすと (2,,1) なので次に先手は (2,,0) を渡せば①に帰着して先手必勝
    • X を減らすと (3,,0) なので次に先手は (2,,0) を渡せば①に帰着して先手必勝
    • Ce を(あるなら)減らすと (3,,0) となり④b)に戻る

後手は Ce を操作するしかないが、それが可能なのも Ce が尽きるまでなので、 なくなったら他のいずれかを操作せざるを得ず、すぐに必敗パターンに持ち込まれる。

②は、後手必勝となる。

  • ②abc) 全てにおいて、
    • 先手が初手 Ce を減らすと (,,1) を相手に渡すことになり、(,,0) が返ってきて負ける
    • 先手は Co を操作するしかない。
  • ②a) の場合、Ce しか操作できない。
  • ②b) の場合、(1,3,0) を渡すことになるので④a)に帰着される。
  • ②c) の場合、(3,,0) を渡すことになるので④b)に帰着される。

いずれでも後手必勝となる。

Python3

C - Range Sums 2

問題文

  • この問題はインタラクティブな問題です。
  • 正整数 N が与えられます。
  • すぬけくんは、(1,2,,N) を並び替えた正整数列 P=(P1,P2,,PN) と、長さ N の正整数列 A=(A1,A2,,AN) を隠し持っています。ここで、P1<P2 が保証されます。
  • あなたは、すぬけくんに 2N 回までクエリを送ることができます。クエリは以下のようなものです。
    • 1s,tN なる相異なる正整数 s,t を指定し、max(Ps,Pt)i=min(Ps,Pt)Ai の値を教えてもらう。
  • P,A を特定してください。

制約

  • 3N5000
  • 1PiN (1iN)
  • PiPj (ij)
  • P1<P2
  • 1Ai109 (1iN)
  • N,Pi,Ai は整数
  • P,A はジャッジとの対話前に決定される

解法

Ai が正なので、(1,2),(1,3),...,(1,j),...,(1,N) をクエリで聞いたとき、 最も大きい値が返る jPj=1 または Pj=N となる。

i  1 2 3 4 5 6    i  1 2 3  4 5 6
P  2 4 6 5 3 1    A  1 9 2 25 2 9

クエリ  (Pi,Pj)                    返値
(1,2) → (2,4)         [----]        36
(1,3) → (2,6)         [--------]    47  ← j=3 の時最大
(1,4) → (2,5)         [------]      38     ⇒P3 が1,Nのどちらか
(1,5) → (2,3)         [-]           11     (この例では N だが、
(1,6) → (2,1)       [-]             10       この時点では回答側にとっては不明)

今度は j を主軸として (j,2),(j,3),...,(j,N) をクエリで聞くと、 返値の昇順が j から近い順となる。
また、差分を取っていけば、最初の2つ以外の Ai は分かる。

クエリ  (Pi,Pj)                    返値
(3,1) → (6,2)         [--------]    47(←実際は上で聞いているので聞かなくていい)
(3,2) → (6,4)              [---]    36
(3,4) → (6,5)                [-]    11
(3,5) → (6,3)           [------]    38
(3,6) → (6,1)       [----------]    48

  i   3  4  2  5  1  6
  P   1  2  3  4  5  6  (または 6 5 4 3 2 1)
返値    11 36 38 47 48

  i   1  2  3  4  5  6
  A   [11] 25  2  9  1  ←返値の差分から計算

A1,A2 に関しては和しか分からないが、ここまでわかれば1回の追加クエリで判別できる。
ここまでで 2N2 回のクエリなので間に合う。

最後に P を復元し、P1>P2 となっていたら反転すれば良い。

Python3

D - Fraction Line

問題文

  • 正の有理数 x に対し、f(x) を以下のように定義します。
    • x を互いに素な正整数 P,Q を用いて PQ と表したときの P×Q の値
  • 正整数 N と、長さ N1 の正整数列 A=(A1,A2,,AN1) が与えられます。
  • 以下の条件をすべて満たす長さ N の正整数列 S=(S1,S2,,SN) を 良い数列 と呼ぶことにします。
    • 1iN1 なるすべての整数 i について、f(SiSi+1)=Ai が成り立つ。
    • gcd(S1,S2,,SN)=1
  • 数列の スコア を、その数列に含まれる要素の総積と定義します。
  • 良い数列の個数は有限個であることが証明できます。良い数列すべてに対するスコアの総和を 998244353 で割った余りを求めてください。

制約

  • 2N1000
  • 1Ai1000 (1iN1)
  • 入力はすべて整数

解法

B,C問題と同じ配点とは思えない難度だが、得意な人は得意なのかも。

まず、サンプル1から良い数列の例を見る。
Si,Si+1 で隣り合う数の比が、Ai になっていればよさそう……?

A     1   9   2   2   9

S   2   2  18   9  18   2
         x9  /2  x2  /9      ← Si から Si+1 にするための計算

S  18  18   2   1   2  18
         /9  /2  x2  x9

ただ、これは A がいずれも単一の素因数しか持たないからで、合成数ならもう少し違ってくる。

A     6  15  20
S   2   3   5  100
     /2      x4
     x3  /3
         x5  x5

全体として比が Ai になっていなくても、素因数毎の比の積が Ai になっていればよい。
実際、f(x) の定義を考えると、Si×a/b=Si+1 の時、f(SiSi+1)=ab となる。
ただし、ab は互いに素でないといけないので、 たとえば Ai=22 の時、2を a にも b にも分けるということはできない。

要は Ai の素因数の種類毎に、1つ前の数に「かける」「わる」の2通りの選択肢があり、それ毎に異なる S ができる。
上例では、6,15,20 はいずれも 2131,3151,2251 と2種類ずつの素因数を持ち、計6個あるので、26S が存在する。

素因数毎に分解してスコアを考える。

  • B(p):=A から素因数 p のみを取り出した数列。
    • 例) A=(6,15,20)B(2)=(2,1,4)
  • Tp:=Tp,iTp,i+1 の比が B(p)i となるようにした数列(の1つ)
元のA:  6  15  20

B(2): 2   1   4    Scr    B(3): 3   3   1    Scr    B(5): 1   5   5     Scr
┌( 1   2   2   8 ) 32┐  ┌( 1   3   9   9 )243┐  ┌( 1   1   5  25 ) 125┐
│( 2   4   4   1 ) 32│×│( 1   3   1   1 )  3│×│( 1   1   5   1 )   5│
│( 2   1   1   4 )  8│  │( 3   1   3   3 ) 27│  │( 5   5   1   5 ) 125│
└( 8   4   4   1 )128┘  └( 9   3   1   1 ) 27┘  └(25  25   5   1 )3125┘
                   ---                       ---                       ----
                   200                       300                       3380

この時、各素因数の Tp としてたとえば (2,4,4,1),(1,3,1,1),(5,5,1,5) を選んだとすると、 実際の最終的な S はこれを各項毎に掛け合わせた (10,60,4,5) となる。

S のスコアは各 Tp ごとのスコア((2,4,4,1) などの各項の総積)を掛け合わせたものと一致する。

  • S のスコア(各項の総積): 10×60×4×5=12000
  • Tp ごとのスコアの積: 32×3×125=12000

さらに、ありえる全ての S を考えた時の総和は、以下にまとめられることが分かる。

  • (全ての T2 のスコアの和)×(全ての T3 のスコアの和)×(全ての T5 のスコアの和)

つまり、上例の A=(6,15,20) に対する答えは、200×300×3380=202800000 となる。

素因数 p 毎に「全ての Tp のスコアの和」を求めれば良い。
以下のDPを考える。

  • DP[i,k]:=Tp[i] までを決めて、
    • 以下の条件をともに満たすような
      • Tp[i] までの最小値が 1 である
      • Tp[i+1]=pk となる
    • 全ての Tp[:i] の総積の和

Tp[i] までは最小値が1であることを保証するが、次に置く予定である k は負であってもよいとし、遷移時に調整する。

i1 から i の遷移へは、C(p)=logpB(p) として、基本的には Tp,ipk を置いて

  • DP[i,k+C(p)i]+=DP[i1,k]×pk
  • DP[i,kC(p)i]+=DP[i1,k]×pkC(p)i0 の場合)

とするが、k が負の場合は最小値が1未満にならないよう、これまでの i 個全体を底上げしてから遷移する。

また、B(p) が1の場合は同じ値が続くため、1 以外になるindexだけ遷移を考えるなどで高速化できる。

Amax 以下の整数が持つ相異なる素因数の最大数を ω とすると、O(N2ωlogAmax) で全体を求められる。

Python3

E - Snuke's Kyoto Trip

問題文

  • 整数 W,H,L,R,D,U が与えられます。
  • 2 次元平面上に京都の町があります。
  • 京都の町には、以下の条件をすべて満たす格子点 (x,y)1 つずつ区画が存在します。それ以外の点に区画はありません。
    • 0xW
    • 0yH
    • x<L または R<x または y<D または U<y
  • すぬけくんは、以下のように京都の町を旅しました。
    • まず、区画を 1 つ選んでそこに立つ。
    • その後、0 回以上好きな回数以下の操作を行う。
      • x 軸正方向または y 軸正方向に 1 移動する。ただし、移動後の点にも区画がなくてはならない。
  • すぬけくんが通った経路としてあり得るものの個数を 998244353 で割った余りを求めてください。

制約

  • 0LRW106
  • 0DUH106
  • 区画が 1 つ以上存在する
  • 入力はすべて整数

解法

矩形のグリッドの中に矩形の穴が空いたようなマス目において、始点終点が自由で、右と上だけの移動で到達できるような経路数を数える。

穴が空いていない場合

(0,0)(w,h) の穴が空いていないグリッドでの答えの求め方を確認しておく。

右と上の移動回数をそれぞれ i,j0iw,0jh)と固定すると、

  • 経路数は \binom{i+j}{i}
  • 始点の選び方は (w-i+1)(h-j+1)

よって、これらの総和を取って以下になる。

  • \displaystyle \sum_{i=0}^{w} \sum_{j=0}^{h} \binom{i+j}{i} (w-i+1)(h-j+1)

これをWolfram Alphaさんに投げると、Σを除去して(階乗の事前計算の元で)O(1) で算出可能な形にしてくれる。

  • f(w,h):=\dbinom{w+h+4}{w+2}-(w+2)(h+2)-1

また、後で必要になるので以下も計算しておく。
これは、「始点は (0,0) 固定で」終点のみ好きな位置で移動を終える場合の経路数となる。

  • g(w,h):=\dbinom{w+h+2}{w+1}-1

なおこれらの式変形は、ホッケースティック恒等式から導ける。

穴が空いている場合

穴の境界に沿って、格子点を8つに分類する。

+++++++      ⑥  |⑦  |⑧
+++++++      ____|____|______
++    +++  →  ④  |    |⑤
++    +++      ____|____|______
+++++++      ①  |②  |③
+++++++          |    |

まず、穴に関係ない(始点と終点を対角線とする矩形が穴に被らない)答えを求める。重複に注意しつつ、以下で求められる。

  • ①②③を連結した矩形の f(w,h)
  • + ①④⑥を連結した矩形の f(w,h)
  • + ③⑤⑧を連結した矩形の f(w,h)
  • + ⑥⑦⑧を連結した矩形の f(w,h)
  • - ①の矩形の f(w,h)
  • - ③の矩形の f(w,h)
  • - ⑥の矩形の f(w,h)
  • - ⑧の矩形の f(w,h)

次に、始点と終点を対角線とする矩形が穴に被るような経路を求める。
穴の左上・右下から45度の斜め線を伸ばした上にある格子点を1つ固定する。影響を受ける経路は、必ずこのどれかを1つだけ通過する。

○++++++
+○+++++    ○: 斜め線を延ばした上にある格子点
++    +++    ◎: 今回の例で着目する格子点
++    +++
++++◎++
+++++○+

g(w,h) を使って、◎の左下・右上に広がる2つの矩形の経路数を求め掛け合わせると、◎を通過する経路数となる。

        +++
        +++
        +++
        +++
++++◎++
+++++

ただし、この中には「穴に関係ない」で数えた経路も重複して数えられているので、包除原理で除く。

        +++             ・・・             +++             ・・・
        +++             ・・・             +++             ・・・
        +++  -          ・・・  -          +++  +          ・・・
        +++             ・・・             +++             ・・・
++++◎++     ++++◎++     ・・・・◎++     ・・・・◎++
+++++         +++++         ・・・・+         ・・・・+    

これで、穴を迂回しつつ◎を通過する経路数が求められる。斜め線上の格子点それぞれについて求めればよい。

Python3

programming_algorithm/contest_history/atcoder/2025/0209_arc192.txt · 最終更新: 2025/02/14 by ikatakos
CC Attribution 4.0 International
Driven by DokuWiki Recent changes RSS feed Valid CSS Valid XHTML 1.0