ごりちゃんがゆく

競プロをするゴリラの精進記録

ABC334-B Christmas Trees を場合分けもできるだけ数学的な頭も使わない解き方を考えてみる

1日に2本も記事を書くなんて珍しいな! クリスマスイブだしな!
ってなわけで書いてみる!

昨日の ABC334-B ですが、ABCのB問題にしては「数学」をしましょうって感じでつらかったと感じた人も多かったのではないでしょうか
ゴリもそこそこ混乱しました

そこで、

  • 場合分けをしない
  • 数学的な頭をできるだけ使わない

解き方を考えてみました
「できるだけ」なので、これでも難しい可能性もありますし、もっと良い方法もあるかもしれません。
図をかいてみたので数学の頭でなく感覚でつかんでもらえるとうれしいです!

問題概要

数直線があります。座標Aを起点に、Mおきにクリスマスツリーが置かれます。座標L,R の間 (両端も含む、L \le R) に、クリスマスツリーは何本ありますか?
A,M,L,R は クソデカなのでループを使ってはいけません。
ABC334-B リンク

ゴリの考えたさいきょうの解き方

3ステップあります

1. Ax=0 の位置に平行移動させる

公式解法の考え方も初手これでした。
(ゴリはコンテスト中は L0 のところに移動してしまったためちょっと大変でした)

平行移動

これをすると、クリスマスツリーの位置が M の倍数のところになるので、かなり見通しがよくなります。 (本番で思いつきたかった...)
なお、必ずしもこの図の通りではありません (LAの右側にあったり、RAの左側にあったりします) が、いずれであっても以降の考え方を適用できます

2. L',R' を、M で割る!

Mで割ります!
これをすることによって、世界全体が  \frac{1}{M} になります!
下の図のように、0を中心に、世界がぐにゃっと縮みます!

Mで割る

これによって、座標Mごとに生えていたクリスマスツリーは、座標1ごとに生えていることになり、
つまり 整数の座標に 生えていることになります!
ただし、注意点として、L''R''\frac{L’}{M}\frac{R’}{M} といった 有理数となります (整数とは限らない)
(さらに実装上の注意なのですが、PythonL/M のように演算すると float型となり、精度が足りなくてWAとなります、回避する方法を後述します)

3. L''を切り上げ、R'' を切り捨てし、整数にする

L''R''が中途半端な位置にあるので、それぞれ切り上げ・切り捨てし、整数にしましょう!
これによりL'''R'''の間にいくつ整数があるか、計算できます!

整数にする

これでほぼ答えは出ているのですが、最後に注意としては
クリスマスツリーはL'''R'''の両端を数えるので、R-L で求まる距離に 1 を足しましょう!
(上の図だと、R'''-L''' は4だけど、整数は端点を含むと5つある)

ACコード (その1)

A,M,L,R = map(int,input().split())
L -= A
R -= A
from decimal import Decimal
L = Decimal(L) / Decimal(M)
R = Decimal(R) / Decimal(M)
from math import ceil,floor
L = ceil(L)
R = floor(R)
print(R - L + 1)
  1. 2~3行目で LRを平行移動しています
  2. 4~6行目で LRをそれぞれMで割っています
    (先述の通り、単に L/M などとすると精度がたりないので、Decimal で精度の良い計算をしています)
  3. 7~9行目で切り上げ、切り捨てをしています

submission

ACコード (その2)

A,M,L,R = map(int,input().split())
L -= A
R -= A
from fractions import Fraction
L = Fraction(L, M)
R = Fraction(R, M)
from math import ceil,floor
L = ceil(L)
R = floor(R)
print(R - L + 1)

その1とほぼ同じですが、2. のところは Fraction でもできます!

submission

おわり

もっとシンプルな解法などあれば教えてくださるとうれしいです!