https://www.acmicpc.net/problem/13977
Baekjoon Online JudgeBaekjoon Online Judgewww.acmicpc.net본인은 파이썬으로 PS하는데, 알법한 애들은 다들 알다 싶이 PyRival이라고 유명한 파이썬 용 PS 라이브러리가 있음.
난 이걸 적극적으로 활용하고 고쳐서 잘 써먹고 있는데, 가끔 날먹급이다 싶은 문제들은 좀 공부가 안되는 것 같아서 풀이를 일부러 자세히 적으려고 함.
이 문제가 딱 그런류였음. 템플릿에 있는 make_nCr_mod 긁어다가 쓰면 딱 풀리는 문제인걸 되게 옛날부터 알고 있었어서
풀이 쓰기가 귀찮으니 안 풀고 미루다가 아까 풀어냈는데, 풀이도 재미가 없어서 시간복잡도를 분석했더니 생각보다 인사이트가 많아서 글 적음.
def gnCr(max_n, mod=10**9 + 7):
max_n = min(max_n, mod - 1)
fact, inv_fact = [0] * (max_n + 1), [0] * (max_n + 1)
fact[0] = 1
for i in range(max_n):
fact[i + 1] = fact[i] * (i + 1) % mod
inv_fact[-1] = pow(fact[-1], mod - 2, mod)
for i in reversed(range(max_n)):
inv_fact[i] = inv_fact[i + 1] * (i + 1) % mod
def nCr(n, r):
if n < r: return 0
res = 1
while n or r:
a, b = n % mod, r % mod
if a < b: return 0
res = res * fact[a] % mod * inv_fact[b] % mod * inv_fact[a - b] % mod
n //= mod
r //= mod
return res
return nCr
일단 내가 썼던 템플릿(원본이랑 좀 다름)
그리고 아래가 풀이임
katex 렌더링 때문에 이미지인거 ㅈㅅ
풀이는 넘기고, 시간복잡도를 구해보기로 함.
처음에 구해본 시간복잡도 식이 O(X + M(max(log_p n, log_p k))) 였는데,
이게 계속 수식을 보면 볼수록 개선점이 보이고,
결과적으로 수식이 간결해지는게 재밌어서 그 과정을 다 적어놨음 ㅋㅋㅋ
이게 진짜 수학 태그 달린 문제의 재미다 싶더라.
솔직히 지금도 너무 간결해서, 틀린거 아닌가 싶기도 함... 고수분들 지적해주면 ㄳ
어쨌든 클래스 밀다가 수학문제 보이면 한번 시간복잡도까지 쭉 구해보는것도 의미 있는거 같음.
PS 개꿀잼
대충 맞는데 log_p(X)가 아니고 log_2(X)일거임 저게 p랑 관련 있는게 아니고 빠른 거듭제곱 계산과 관련 있는거라
아 역시 그런가? 루프 돌때마다 p씩 줄어들어서 log_p라고 생각했는데 log_2가 맞나보네
쿼리는 log_p(X)가 맞고, 전처리가 X log (X-2) 인거 아님? pow(a, b, c)로 역원 구하면 a^(b-2)해야 하잖아. 그래서 역원을 구할때 log(X-2)만큼 더걸려서 X log (X-2)인거 같은데
이거 쿼리에서 while문 몇번까지 돌아가는지 직접 세보면 log_p(X)인지 log X인지 알거같음 ㄱㄷ
아니 X log (p-2)지 참 ㅅㅂ
아.. 전처리가 X log (mod-2) 인 걸 수도 있겠네 어쩐지 내가 구한 시간복잡도로는 너무 오래걸리는거 같았는데
400만까지 랜덤데이터 10만개 넣어서 10만번 호출된거 봐선 log_p(X)가 맞는듯?
정확히 이 구현만 놓고 보면 O(X log (p-2) + M log_p X) 가 맞는거 같음. 내 생각엔 쿼리가 10만개니 저 pow를 쿼리에 넣어서 쿼리당 O(log_p X log (p-2)) 만큼 걸리게 하는게 더 빠를 것 같다고 봄
여긴 진짜 개빡고수들밖에 없네.. 진짜 ㄳㄳ
지금보니 전처리가 O(N + log p)네.. 내 눈은 어떻게 돼먹은거지
해당 댓글은 삭제되었습니다.
ㄴㄴ 그냥 VS코드로 주피터 노트북 돌려서 저장하고 깃에다가 올려둠
이 문제에서는 X가 P보다 작아서 쿼리당 O(1)에 되는거?
Lucas Theorem이 해당 시간복잡도를 잘 설명해주니 언젠가 시간 나면 찾아보기 바람. 현재 전처리 시간은 loop 도는 거 + 빠른 거듭제곱이니까 O(X + logP) 인데, extended euclid 쓰는게 순수 시간복잡도 면에서는 나을 수도?
덧붙여서, 팩토리얼 역원의 배열을 초기화하는 색다른 방법이 있음.
https://koosaga.com/63