1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

AtCoder ABC384 Dが個人的にめっちゃ難しかったので解説(python)

Last updated at Posted at 2024-12-15

ABC384 D問題

解説みたけど理解できませんでした...。

なので、私のような数学が苦手な人でも理解できるよう解説します。

部分ごとに考える

まず、部分ごとに分けて考えましょう。

入力例1を例に挙げると、以下のように分けられます。

explain_384d.png

この繰り返しの部分は今回の場合8から始まっており、8,4,3を繰り返しています。
この部分は、繰り返しの開始の数字が何であっても、

繰り返した回数×配列Aの総和になります

今回は、8,4,3を2回繰り返しているので15 × 2で30になっています。
もし、3から始まり、これを二回繰り返していたとしても、
3,8,4を二回繰り返すので、15 × 2で30になります。

では、これが何を意味するかと言うと、今回の問題で考えるべきは、
sとAの総和の余り
のみということになります。

なぜなら、繰り返しの部分の値は常に一定になるからです。
なので、その値にAの部分和を足し、sの値にすることに集中できます。

n,s = map(int,input().split())
a = list(map(int,input().split()))

find_num = s % sum(a)

find_numはsとAの総和の余りです。
今回の問題は、言い換えると「Aの部分列の総和==find_numを探す」です。

累積和を取ろう

Aの範囲の総和なので累積和を取りましょう。

a *= 2
rui = [0] * (n * 2)
tmp = 0
for i in range(2 * n):
    rui[i] = a[i] + tmp
    tmp += a[i]

累積和を取ったため、rui[j] - rui[i]の値がfind_numかどうかで判定が出来ます。

では、なぜ二倍の累積和を取ったかというと、

入力1ではAの入力は、n = 3 , s = 42 , a = [3,8,4]でしたが、
もし、sの値が37だとしたら、s%sum(a)=7なので、3と4を選ばなければ答えは出ません

無限数列なので、3と4は、二週目以降4,3の順番で繋がります
そのため、その時の判定もするために二倍のサイズで累積和を取りました。

rui[j] - rui[i]の値を求めまくろう。

ここまで来たら、あとは条件を満たす値を探索するだけです。
求め方はたくさんありますが、私の求め方を紹介します。

ans = "No"
l = 0
r = 0
while(r < 2 * n):
    now = rui[r] - rui[l]
    #小さかったらrを増やして広げる
    if(now < find_num):
        r += 1
    #大きかったらlを増やして縮める
    elif(now > find_num):
        l += 1
    #同じだったら正解だ
    elif(now == find_num):
        ans = "Yes"
        break

やっていることは、見ての通りです。

あとがき

初投稿です。
ここまで見てくださり、ありがとうございました。

拙い部分も多くあったと思います。
コメントなどでご指摘・ご質問などございましたらお気軽にしてください。

1
1
1

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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?