N면체 주사위의 숫자 합이 S라고 하자.
M개의 주사위의 각 기댓값의 합을 기약분수로 나타낼 때, 그 값을 1,000,000,007로 나눈 나머지 값을 출력하는 문제
문제
실제로 존재하는지 아닌지는 차치하고, 당신에게 삼면체 주사위가 있어서 이 주사위를 굴린다고 생각해보자. 주사위를 굴렸을 때 각 면이 나올 확률은 모두 동일하게 1/3 이다. 한 면에는 1, 다른 한 면에는 2, 남은 한 면에는 4가 적혀있다고 하면 주사위를 굴렸을 때 나오게 되는 숫자의 기댓값은 과연 몇일까? 간단하게도 셋의 평균인 7/3이 될 것이다.
이 문제를 조금 확장해서, "N면체 주사위의 각 면에 적힌 수가 주어졌을 때, 주사위를 굴렸을 때 각 면이 나올 확률이 모두 같다면 주사위를 굴렸을 때 나오게 되는 수의 기댓값은 과연 몇일까?"라는 문제가 주어졌다고 하자. 위의 예시에 대한 답을 소수로 출력한다면 2.33333333...일텐데, 무한한 자릿수를 모두 출력할 수는 없으니 적당히 끊어서 출력할 것이고, 이 끊긴 소수를 채점 프로그램이 다시 입력받아서 정답과 비교한다고 하면 결과가 얼마나 부정확할 것인가? 그렇기에 답을 정확히 판별하기 위해 출력하고자 하는 분수를 기약분수로 만들어 분모와 분자를 직접 출력하도록 했던 시기가 있었다.
이제 문제를 조금 더 확장하여, M개의 주사위가 있어서 이 중 i번째 주사위가
$S_1/N_1 + S_2/N_2 + ... + S_M/N_M$
즉, 각 주사위에서 나오게 되는 수의 기댓값을 모두 더하면 답이 되는 것이다. 이 답을 정확하게 출력하기 위해, 모든 분수
어떤 분수가 기약분수로 나타냈을 때 a/b이면, 이 분수는 a × b-1 mod X
b의 모듈러 곱셈에 대한 역원 b-1은 대체 어떤 수인 것일까? 이 수는 다음과 같은 성질을 만족하는 정수이다.
b-1 × b ≡ 1
소수 모듈러에서만 성립하는 페르마의 소정리에 의해 bX - 1 ≡ 1
이해를 돕기 위해 X를 11로 두고 Q = 7/3 을 계산해보자. 3-1 ≡ 4
분수
그러나 이 방법에도 문제가 있는 것은 마찬가지이다. 앞의 예에서 7/3을 6으로 저장했지만, 그냥 6/1도 6으로 저장할 것이다. 즉 서로 다른 두 분수도 모듈러 상에서 같은 정수로 저장하여, 정확한 판별을 한다는 우리의 목적에 부합하지 않는 것이다. 또다른 문제로는, 분모가 소인수로 X를 가질 때에는 역원을 계산할 수 없어서 모듈러로 나타낼 수가 없다는 점이 있다. 이러한 문제를 해결하기 위해 모듈러를 1,000,000,007와 같은 큰 소수로 하는데, 이를 통해 서로 다른 두 분수가 같은 정수로 나타나게 되는 확률을 낮추고, 분모가 가질 수 있는 소인수의 범위를 늘리는 효과를 볼 수 있다. 그는 이런 방식이 그래도 가장 정확한 방식이라고 생각하게 되었다.
이제 이 방식으로 M 개의 주사위가 있고, i번째 주사위가
입력
첫 번째 줄에는 주사위의 수를 나타내는 정수 M
다음 M개의 줄은 각 주사위의 정보를 나타내며, 이 중 i
출력
모든 주사위를 한 번씩 던졌을 때 나온 숫자들의 합의 기댓값을 출력한다. 정확한 판별을 위해, 답을 기약분수로 나타내었을 때 a/b가 된다면,
분수의 합을 분모를 통분해서 구하고, 유클리드 호제법
페르마의 소정리 $b^{x-2} ≡ b^{-1} modx$를 적용해서 b의 역원을 구해서 문제를 풀었다.
import sys
input = sys.stdin.readline
mod = 1_000_000_007
M = int(input())
ans = []
def irr_frac(a, b, c, d):
A, B = (a*d+b*c)%mod, b*d%mod
q, r = A, B
while q%r:
q, r = r, q%r
return A//r, B//r
def inverse(b, x=mod-2):
if x == 1:
return b
if x%2:
return (b*pow(inverse(b, x//2), 2, mod)) % mod
else:
return pow(inverse(b, x//2), 2, mod)
a, b = map(int, input().split())
for _ in range(M-1):
N, S = map(int, input().split())
a, b = irr_frac(a, b, S, N)
print(((a%mod)*(inverse(b)%mod)) % mod)
예제 입력 1 복사
1
3 7
예제 출력 1 복사
333333338
힌트
모듈러가 11에서 1,000,000,007이 되어 답이 달라졌지만, 역시 3을 곱한 다음 1,000,000,007으로 나눈 나머지는 7이 된다.
'IT > Python' 카테고리의 다른 글
[백준] 1987번 알파벳 [Python] 0 | 2023.12.25 |
---|---|
[백준] 10830번 행렬 제곱 [Python] 1 | 2023.12.22 |
[백준] 2448번 별 찍기 - 11 [Python] 0 | 2023.12.20 |
[백준] 1504번 특정한 최단 경로 [Python] - 데이크스트라 0 | 2023.12.19 |