LoginSignup
1
1

More than 3 years have passed since last update.

AtCoder Beginner Contest 152に参加したので、着手した問題の考察を書いていきます。結果は遅めの E 完でした。

A - AC or WA

問題概要: 非負整数 N, M が与えられる。N 個のテストケースがあり、すべてのテストケースに正解した提出は AC となる。ある提出は、そのうち M 個のテストケースに正解した。この提出が AC であるか判定せよ。

A: 考察

A問題にしばしばある、問題文を読解する問題です。(問題概要をどう書くか悩む。)

AC になる条件「すべてのテストケースに正解」を「N 個のテストケースに正解」と言い換えると、「M 個のテストケースに正解した」提出が AC かどうかは M == N と同値であることが分かります。

筆者の回答 (Rust, AC): https://atcoder.jp/contests/abc152/submissions/9617938

B - Comparing Strings

問題概要: 1桁の整数 a, b が与えられる。a を b 個並べた文字列と、b を a 個並べた文字列のうち、辞書順で小さい方を出力せよ。

B: 考察

文字列の辞書順 (辞書式順序) は、2つの文字列の前から1文字ずつ比較し、最初に異なる文字の大小を文字列の大小とみなす順序です。

前方に小さい文字を含む文字列ほど辞書順では小さいので、a < b なら「a を b 個並べた文字列」の方が「b を a 個並べた文字列」より辞書順で小さくなります。

a > b のケースも同様で、a = b のケースは自明です。

もう少し考えると、「min(a, b) を max(a, b) 個並べた文字列」を出力すればよいことが分かり、場合分けが減ります。

筆者の回答 (Rust, AC): https://atcoder.jp/contests/abc152/submissions/9606372

C - Low Elements

問題概要: 正の整数 N と、1 以上 N 以下の整数の順列 P が与えられる。以下の条件を満たす 1 以上 N 以下の整数 i の個数を数えよ。

条件: 1 以上 i 以下のすべての整数 j について、P(i)≤P(j) が成り立つ

制約: N≤2×10^5

C: 考察

例えば N=4 なら、各 i が満たすべき条件は以下のようになります。(j = i の場合は常に成立するので省略。)

  • i=1 無条件成立
  • i=2 P(2)≤P(1)
  • i=3 P(3)≤P(1) かつ P(3)≤P(2)
  • i=4 P(4)≤P(1) かつ P(3)≤P(2) かつ P(4)≤P(3)

ようするに P(1), P(2), ..., P(i-1) がすべて P(i) 以上なら条件を満たします。

i についてループを回し、内側で j についてループを回す2重ループを書けば解けそうに見えますが、これは不正解です。全体で100億回以上の計算が必要になり、実行時間制限(2秒)を超過するからです。

「すべてが X 以上」は「最大値が X 以上」と言い換えられます。そのため、i について 1 からループを回す際に「最大値」を変数としてもっておくと、各 i が条件を満たすか高速で判定できます。

筆者の回答 (Rust, AC): https://atcoder.jp/contests/abc152/submissions/9604044

D - Handstand 2

問題概要: 正の整数 N が与えられる。N 以下の正の整数 A, B の順序対で、以下の条件を満たすものを数えよ。

条件: A の末尾の桁が B の先頭の桁に等しく、A の先頭の桁が B の末尾の桁に等しい。(10進数で表記する。0 は先頭の桁にしない。)

D: 考察

例えば A = 31415 なら先頭・末尾の桁はそれぞれ 3, 5 なので、B が 5...3 という形の整数 (例えば 53, 513, 523, など) なら (A, B) は条件を満たします。

(A, B) が条件を満たすとき、A ≠ B なら (B, A) も数えなければいけません。A < B のケースを数えて2倍し、A = B は別途数えるのがよさそうです。

整数に関して問題になるのは (先頭の桁, 末尾の桁) だけなので、これが等しいもの同士は区別せずにまとめて考えてよいです。

  • 35, 305, 31415, ... → (3, 5)

結局、以下の手続きにより数え上げられます。1 以上 N 以下の整数 B (先頭・末尾は x, y) を小さい順に列挙し、整数を (先頭の桁, 末尾の桁) ごとに表 map でカウントしていきます。map には B 未満の整数だけがカウントされているので、(A, B) のペア (ただし A < B) は map(y, x) 通りです。

(B, A) もあるので、計上するのは map(y, x) * 2 です。x == y のときは (B, B) もありうるのでさらに +1。

筆者の回答 (Rust, AC): https://atcoder.jp/contests/abc152/submissions/9617788

E - Flatten

問題概要: N 個の正の整数 A(1), ..., A(N) が与えられる。以下の条件を満たす正の整数 B(1), ..., B(N) のうち、総和 B(1) + ... + B(N) の最小値を求めよ。(1000000007 で割った余りを出力)

条件: すべての i < j について、A(i)・B(i) = A(j)・B(j) が成り立つ。

制約: N≤10^4, A(i)≤10^6

E: 考察

条件は言い換えると A(1)・B(1) = A(2)・B(2) = ... = A(N)・B(N) です。この値を X とおくことにします。

A(i)・B(i) = X から、X はすべての A(i) の公倍数です。そのような X で B の総和を最小にするには、最小公倍数を選ぶのがベストです。

問題は X の値が巨大であり、64ビット整数では表現できないことです。(任意精度整数を使えば計算できるという噂もある。)

掛け算と割り算しかしないので、対数 (のようなもの) を使うことで数字を小さくできます。

素因数分解の一意性から X = 2^p(1)・3^p(2)・5^p(3)... となるベクトル p が一つだけ存在します。これを X の素数指数表現と呼んで L(X) と書くことにします。

素数指数表現は対数のようなもので、整数の掛け算はベクトルの足し算になり、割り算は引き算になり、最小公倍数は成分ごとの max になります。

    L(X * Y) = L(X) + L(Y)

    L(X / Y) = L(X) - L(Y) (ただし X は Y の倍数)

    L(LCM(X, Y)) = max(L(X), L(Y))) (ただしベクトルの max は成分ごとの max)

そういうわけで L(B(i)) = L(X/A(i)) = max(L(A(1)), ... L(A(N))) - L(A(i)) を計算して、整数に戻せば OK です。

整数 n の素因数分解は、n を 2 以上 √n 以下の各整数 i で小さい順に割っていくことにより、全体として O(√n) 時間でできます。

i で m 回 (m≥1) 割れたとき、もとの n は「素因数 i を m 個含む」といえて、最終的に n≥2 なら「素因数 n を1個含む」といえます。

小さい順に割っていくのが重要で、このおかげで i が素数かどうか調べる処理が不要になります。i が素数のとき n から素因数 i がすべて消えるので、i が合成数のときは n を1度も割れないからです。ループ終了後の n も √n 以下の素因数を持たないことから素数といえます。

筆者の回答 (Rust, AC): https://atcoder.jp/contests/abc152/submissions/9621481

参考: 「互いに素」という概念

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1