Processing math: 23%

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

AtCoder Regular Contest 191 (Div. 2)

A,B問題の2完だけだと最速で解いてもPerf.ギリ2000程度で、遅解きの場合はPerf.1200ちょいまで下がる。 つまり水~青帯の人もこの辺は解いているということで、実力の高まりを感じる。
C~Dを解ききらないとどうにもならんね。

Div.2なら、時間があれば全く解けないという感じではないので、考察と実装の効率化が肝か。

A - Replace Digits

問題文

  • 長さ N の文字列 S と長さ M の文字列 T が与えられます。ここで、 S,T はどちらも 1 から 9 までの数字からなります。
  • あなたは以下の操作を k=1,2,,M の順に行います。
    • 1iN を満たす整数 i を選ぶ。そして、 Si 文字目を Tk 文字目で置き換える。
  • M 回の操作が終わった後の文字列 S を整数としてみた値の最大値を求めてください。

制約

  • 1N,M106
  • N,M は整数
  • S は 1 から 9 までの数字からなる長さ N の文字列
  • T は 1 から 9 までの数字からなる長さ M の文字列

解法

しっかり考えないと見落としパターンが発生する、やな問題。

まず、T にある大きい数から順に、S の先頭から、置き換えた方が答えが大きくなる場合、その位置を「予約」していく。

i  1 2 3 4 5 6
S  3 1 7 1 5 9
T  2 6 5 8 5 2

Tで大きい順に、
8 は、S[1]=3 より大きいので、i=1を予約する
6 は、S[2]=1 より大きいので、i=2を予約する
5 は、S[3]=7 より大きくない
      S[4]=1 より大きいので、i=4を予約する
5 は、S[5]=5 より大きくない
      S[6]=9 より大きくない    i が S の末尾に達したので終了

よって、この例では T の大きい方から3個を使って、以下の S のように置き換えられる(途中段階)。

S'  8 6 7 5 5 9
    ~ ~   ~

ただし、T の要素は「必ず」置き換えなければならないので、末尾の TM=2 もどこかを置き換えないといけない。
(末尾以外の要素は、もし不要であっても、たとえば TM を置き換える予定の位置を置き換えておけば、最後に上書きされるので気にしなくてよい)

上例の場合は、末尾を置き換えるのが最適となり、これが最終的な答えとなる。

S''  8 6 7 5 5 2
               ~

TM 自体がどこかを予約した数であればいいが、そうでない場合、どこを置き換えるのが最適か?

S の最小値を m としたとき、

  • m<TM なら、TM はアルゴリズム上、どこかを予約しているはずである。S から変える必要は無い。
  • m=TM なら、TM はどこかを予約しているか、または最初から S にあった要素となる。そこを置き換えれば S から変える必要は無い。
  • m>TM なら、S から減少するがどこかを上書きしないといけない。末尾を置き換えるのが最も被害が少ない。

これで答えとなる。下線部分を見落としやすい。

Python3

B - XOR = MOD

問題文

  • 正整数 N,K が与えられます。
  • 以下の条件を満たす正整数 XN と相性の良い数 と呼びます。
    • XN の排他的論理和は、XN で割ったあまりと等しい。
  • N と相性の良い数が K 個以上存在するか判定し、存在する場合は N と相性の良い数のうち K 番目に小さい値を求めてください。
  • T 個のテストケースが与えられるので、それぞれについて答えを求めてください。

制約

  • 1T2×105
  • 1N,K109
  • 入力される値は全て整数

解法

考察の取っかかりやすさはAより簡単?

相性の良い数 X の性質を考えていくと、

  • 必ず XN である。
    • X<N の時、X \mod N = X である一方、1以上の N に対して X \oplus NX のままになり得ない。
  • 2進数の桁数が同じである。
    • 2進数の桁数が d(X) \gt d(N) の場合、X の最上位bit はそのまま残り、X \oplus N \gt N である。
    • しかし、剰余の性質上、X \mod N \lt N であり、矛盾する。

ここから、N \le X \lt 2N となるので、X \mod N = X - N と同じと考えて良いと分かる。

すると、相性の良い数とは、 「N を2進数で表したときに“0”である部分のいくつかを“1”に置き換えた数」と同じと言える。

N  1001101001111111000001110
    ~~  ~ ~~       ~~~~~   ~
X候補
   1011101001111111110111110
   1001101011111111110101111  など

この中の K 番目の要素を求めたい。

相性の良い数は、N の2進数表記の“0”の個数を k とし、2^k 個だけ存在する。2^k \lt K なら存在しない。

存在する場合、K-1 を2進数で表記し、それぞれを N の“0”である部分に下から対応させていった数が答えとなる。

N   1001101001111111000001110    (10進数表記: 20250126)
           ||       ||||`--,|
           ||       |||`--,||
           ||       ||`--,|||
           ||       |`--,||||
           ||       `--,|||||
           |`---------,||||||
           `---------,|||||||
K-1                  10111110   (10進数表記:      190)
             ↓
X   1001101101111111111111110

Python3

C - A^n - 1

問題文

  • 1 以上 10^9 以下の正整数 N が与えられます。
  • 以下の条件を共に満たす正整数の組 (A,M) を一つ求めてください。ただし、制約下でそのような正整数の組が必ず存在することが証明できます。
    • A,M はどちらも 1 以上 10^{18} 以下の正整数である。
    • A^n-1M の倍数となるような正整数 n が存在し、その最小値は N である。
  • T 個のテストケースが与えられるので、それぞれについて答えを求めてください。

制約

  • 1\le T\le 10^4
  • 1\le N\le 10^9
  • 入力される値は全て整数

解法

Editorialの解法②に相当する方法で解こうとして、デバッグ中に時間切れ。
解法①は天啓がないと思いつけないね。

まず、N=1 の時は “1 1” などいくらでも答えがあるので、以降 N \ge 2 とする。

問題文の条件は原始根、位数の発想と似ている。

「法 P における a の位数」とは、以下を満たす i のことを指す。

  • P を3以上の素数とし、a1 \le a \lt P を満たす数とする
  • a の指数を 1,2,... と増やしていったとき、最初に a^i \equiv 1 \mod{P} となる ia の位数という。

M を素数としてまず決め、次に法 M における位数が N である A を見つければよい。

M の求め方

P における位数は、どのような a でも必ず P-1 の約数となる性質がある。
よって、まずは kN+1 が素数となるような k を探す。

基本的にしらみつぶししかない(と思う)が、当てはまる数はたくさんあるので、乱択や、小さい方から探していけばすぐに見つかる。
素数判定は、10^{18} 以下なら高速な「ミラーラビン判定法」を決定的アルゴリズムとして使える。

見つかったら、M=kN+1 とする。

どのような素数 P に対しても以下の事実が証明されているので、M は1つ見つかればよい。

  • 全ての P-1 の約数 d について、法 P における位数が d である数が存在する。

A の求め方

こちらも M と同様、解析的に解く方法はないので、乱択または小さい方から探していく。

「法 M における a の位数が N」とは、以下に言い換えられる。

  • a^{N} \equiv 1 \mod{M}
  • N 以外の全ての N の約数 d について、a^{d} \not\equiv 1 \mod{M}

ただし約数は全てを確かめる必要は無い。
a^{d} \equiv 1 なら、d の倍数については全て i=2,3,... として a^{id} \equiv 1 なので、 結局、N の素因数を(指数は無視して)p_1,p_2,...,p_m として、

  • a^{N} \equiv 1 \mod{M}
  • 全ての N の素因数に対して、a^{N/p_i} \not\equiv 1 \mod{M}

を確かめ、この判定を通過する aA とすればよい。

十分な確率でACを取れるほど期待実行時間が低いか、テストケースに助けられているだけかどうかは未証明。


「位数が N である a」を直接求めにいくより、 Editorialの解法②のように「法 M の原始根である x」を探して、A=x^k とした方がよかった。
M-1 個ある a の候補中に、ちょうど位数が N となるものはオイラーのトーシェント関数を使って \phi(N) 個しかないので、 N に対して M が大きくなった場合、なかなかヒットしづらい。
位数が M-1 となる原始根を探す方が見つかる確率が高く、期待実行時間も短かったかもしれない。

Python3

D - Moving Pieces on Graph

問題文

  • N 頂点 M 辺の単純連結無向グラフが与えられます。辺 i は頂点 u_i と頂点 v_i を双方向に結んでいます。
  • はじめ、コマ A が頂点 S に、コマ B が頂点 T にあります。ここで、 S,T は入力として与えられます。
  • あなたは以下の操作を好きな順序で好きな回数行うことができます。
    • コマ A かコマ B のどちらかを選び、そのコマを今の頂点から辺で結ばれた頂点に移動させる。
    • ただし、操作後にコマ A とコマ B が同じ頂点に来るような操作はできない。
  • あなたの目的はコマ A が頂点 T に、コマ B が頂点 S にある状態にすることです。
  • あなたが目的を達成することができるか判定し、目的を達成することができる場合は必要な操作回数の最小値を求めてください。

制約

  • 2\le N\le 2\times 10^5
  • \displaystyle N-1\le M\le \min\left(\frac{N(N-1)}2,2\times 10^5 \right)
  • 1\le u_i \lt v_i\le N
  • 与えられるグラフは単純かつ連結
  • 1\le S,T\le N
  • S\neq T
  • 入力される値は全て整数

解法

いろんなケースを注意深く網羅する必要がある。
注意力は要するが、整数論の知識は不要な分、C問題より取り組みやすい。

どこかで2つのコマをすれ違わせないといけない。
パスグラフ(一直線グラフ)の場合は明らかにすれ違えない。以下、グラフはそれ以外の形状とする。

S,T 以外の頂点を以下のように考える。

○○,     ,○        ,○○    左: Tからは、Sを経由しないと行けない頂点
○○- S -○-○-○- T -○○    中: SからもTからも、もう一方を経由せずに行ける頂点
○○'        ○'     `○○    右: Sからは、Tを経由しないと行けない頂点
  • d_S[i]:=S から T を経由せずに行ける頂点 i までの最短距離、行けない場合はINF
  • d_T[i]:=T から S を経由せずに行ける頂点 i までの最短距離、行けない場合はINF

また、P=(S,p_1,...,T) を最短パスの1つとし、その距離を d_{\min} とする。

すれ違うには、以下の2通りが考えられる。

  • (a)「中」の頂点のうち、P に使われない頂点がある場合、一方をそこまで持っていってすれ違わせる
  • (b)「左」または「右」の頂点のうち、次数が3以上の頂点ですれ違わせる

(a)の場合、以下のようにし、移動回数は d_{\min}+d_S[v]+d_T[v] となるので、これが最も小さくなる v を求める。

  • コマAを S→v まで移動させる
  • コマBを P に従って T→S まで移動させる
  • コマAを v→T まで移動させる

d_S,d_T に一方を経由してはいけないという制約を課しているのは、 Aが S→v に移動する際に T は通ってはいけない、v→T に移動する際に S を通ってはいけないためである。

(b)の場合、以下のような感じになる。

...○,               ... A,              ...○,
      ○--A--B-- →        ○--○--  →        ○--B--A--
...○'               ... B'              ...○'

次数3以上で、最も S から近い「左」頂点、または最も T から近い「右」頂点ですれ違わせればよい。
その距離を d_j として、2コマが d_j+1 の距離を余分に往復することになるので、以下が移動回数となる。

  • 2d_{\min} + 4(d_j + 1)

パスグラフで無い場合、(a)(b)のいずれかには必ず当てはまる。小さい方が答えとなる。

Python3

E - Unfair Game

問題文

  • 正整数 N,X,Y と長さ N の非負整数列 A=(A_1,A_2,\ldots,A_N), B=(B_1,B_2,\ldots,B_N) が与えられます。
  • N 個の袋があり、それぞれ袋 1 から袋 N までの番号が付けられています。袋 i にははじめ金貨が A_i 枚、銀貨が B_i 枚入っています。
  • この N 個の袋を使って高橋君と青木君がゲームをすることを考えます。最初に高橋君がいくつかの袋を取り(一つも取らなくても良い)、高橋君が取らなかった袋を全て青木君が取ります。その後、高橋君から始めて 2 人は交互に以下の操作を繰り返します。
    • 自分の持っている袋のうち金貨か銀貨が 1 枚以上入っている袋を選び、選んだ袋に対し以下の 2 つのうちいずれかを行う。
      • 金貨を 1 枚取り除き、銀貨を入れる。ただし、銀貨を入れる枚数は高橋君なら X 枚、青木君なら Y 枚である。この操作は金貨が選んだ袋に 1 枚以上ある場合にのみ行うことができる。
      • 銀貨を 1 枚取り除く。この操作は銀貨が選んだ袋に 1 枚以上ある場合にのみ行うことができる。
    • その後、選んだ袋を相手に渡す。
  • 先に操作ができなくなったプレイヤーが負けとなります。
  • 2 人が自分が勝つために最適な行動を取ったとき、高橋君が勝てるような最初の袋の取り方が何通りあるかを 998244353 で割ったあまりを求めてください。

制約

  • 1\le N\le 2\times 10^5
  • 1\le X,Y\le 10^9
  • 0\le A_i,B_i\le 10^9
  • 入力される値は全て整数

解法

袋が1つの場合

状態が多すぎると考察しづらいので、ひとまず「袋が1つしか無い」場合にどうなるか、以下のような再帰関数で調べてみる。

  • f(a,b,t,x,y):= 金貨から銀貨への両替枚数がそれぞれ x,y 枚で、金貨が a 個、銀貨が b 個入った袋が t=0:高橋君、t=1:青木君に回ってきたときに、どちらが勝つか

すると、以下の事実が分かる。(ちゃんとした証明はともかく、結果の観察から簡単に推測は付く)

  • x,y それぞれの「偶奇」により、a,b,t に対する結果が4通りに分かれる(偶奇が同じものは勝敗状況が同じ)
  • 勝敗は、a,b,t,x,y によって簡単に(O(1) で)判別できる
    • 具体的な判定方法はEditorial参照。証明は数学的帰納法によって可能

袋は、以下の4パターンに分かれる。

  • P: どちらが最初に持っていても高橋君が勝つ
  • Q: 最初に持っていた方が勝つ
  • R: 最初に持っていた方が負ける
  • S: どちらが最初に持っていても青木君が勝つ
最初に持ってる方   高橋  青木
               P    ○    ×     ○: 最初に持ってたら必勝
               Q    ○    ○     ×: 最初に持ってたら必敗
               R    ×    ×
               S    ×    ○

袋が複数の場合

持っていたら勝てる袋を「必勝の袋」、逆を「必敗の袋」とする。
(※ここでの「勝てる」は、袋単位の勝敗であり、その袋が空になった状態で相手に渡せる、という意味とする)

つまり、最初の状態では高橋君にとっての P,Q、青木君にとっての Q,S が必勝の袋となる。

最初に手にする人が決まった時点で、一方の必勝の袋はもう一方の必敗の袋となる。
(そうなるように、必勝側が操作して渡す)

ゲーム中は、単純化すると、以下の応酬が繰り返される。

  • 「必勝の袋」を相手に渡す。相手にとっては「必敗の袋」となる。
  • 「必敗の袋」を相手に渡す。相手にとっての「必勝の袋」となる。

いつか空(=必敗の袋)となって渡された時、その袋に関してはそれ以上操作できなくなる。
つまり応酬の部分をばっさりカットすると、最初に持つ袋を決めた後、それぞれの中身を以下のように変えてから開始しても結果は変わらない。

  • 「必勝の袋」は、銀貨が1枚だけ入った袋
  • 「必敗の袋」は、最初から空の袋

高橋君がゲームに勝つには、「青木君より多くの必勝の袋を最初に持っている」ことが必要十分条件となることがわかる。
それさえ満たされていれば、必敗の袋はどうであってもよい。

つまり、

  • 初期状態の P,Q,R,S の袋の個数をそれぞれ p,q,r,s 個とする(入力から計算できる固定値)
  • うち、高橋君が最初に選んだ袋をそれぞれ i,j,k,l 個とする

としたとき、

  • i+j \gt (q-j)+(s-l) であればよい。
  • 上を満たす (i,j,l) の組毎に 2^r \dbinom{p}{i}\dbinom{q}{j}\dbinom{s}{l} 個の選び方がある。

不等式を整理すると i+2j+l \gt q+s となる。
以下のように二項係数の数列を(j だけは2倍なので1個置きに)定義してこれらを畳み込む。

例)
p=5 → [1,  5, 10, 10,  5,  1]
q=4 → [1,  0,  4,  0,  6,  0,  4,  0,  1]
s=0 → [1]

畳み込んだ結果の q+s+1 以上となる添字部分を合計し、2^r をかければ答えとなる。

Python3

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