문제
- 링크: https://www.acmicpc.net/problem/16998
- 요약: 정수
p, q, n에 대해S = \sum_{i=1}^{n} (p·i mod q)를 구한다. 여러 테스트 케이스가 주어진다. 합 전체에는 모듈러를 취하지 않는다. - 제한:
1 ≤ W ≤ 1e5,1 ≤ p, q, n ≤ 1e6, 시간 5초, 메모리 512MB
입력/출력
| |
예시
| |
접근 개요
- 핵심 관찰:
(p·i mod q) = p·i - q·floor((p·i)/q). 따라서 \[ S = p\cdot\frac{n(n+1)}{2} - q \cdot \sum_{i=1}^{n} \left\lfloor \frac{p i}{q} \right\rfloor \] floor_sum(n, m, a, b) = \sum_{i=0}^{n-1} floor((a·i + b)/m)를 유클리드 호제법으로 O(log m)에 계산 가능.- 또한
g = gcd(p, q)로 나누어p = g·a,q = g·m이면(p·i mod q) = g·((a·i) mod m)이고,gcd(a, m) = 1일 때 길이m의 완전한 주기에서 잔여 합은m(m-1)/2. - 두 방법 모두 가능: (1) 직접 floor_sum로 계산하거나, (2)
g로 분해 후 “완전 주기 + 접두부” 합을 구한 뒤g를 곱한다.
시각화 (개략 흐름)
flowchart TD
A[입력 p,q,n] --> B{g = gcd(p,q)}
B --> C[a = p/g, m = q/g]
C --> D{전부 floor_sum로 계산?}
D -- 예 --> E[p*n*(n+1)/2 - q*floor_sum(n+1,q,p,0)]
D -- 아니오 --> F[full = n div m, rem = n mod m]
F --> G[주기합 = m*(m-1)/2]
G --> H[partial = sum_{i=1..rem} (a*i mod m)]
H --> I[답 = g*(full*주기합 + partial)]
E --> J[출력]
I --> J[출력]
알고리즘 설계
- 방법 A:
S = p*n(n+1)/2 - q * floor_sum(n+1, q, p, 0)floor_sum은 표준(AtCoder Library 스타일) 재귀/반복 구현 사용.
- 방법 B:
g = gcd(p, q)로 분해하여 길이m = q/g주기마다의 합(서로소 조건에서m(m-1)/2)을 이용해O(1)로 처리하고, 남은rem개는floor_sum으로 접두부만 계산. - 위 두 방법의 결과는 동일하며, 구현 난이도 및 상수로 인해 A가 간결, B가 수학적 직관이 명확.
정당성 근거
- 분해식
x mod q = x - q*floor(x/q)에 의해 전체 합을 선형/바닥합으로 분리할 수 있음. gcd(a, m) = 1이면 곱셈에 의한 잔여류 순열이 성립하여 한 주기의 합이0..m-1의 합과 동일(=m(m-1)/2).floor_sum은 유클리드 호제법으로 분자로/분모를 교환하며 문제 크기를 감소시키므로 각 단계가m을 줄여서O(log m)에 종료.
복잡도
- 시간: 케이스당
O(log q) - 공간:
O(1)
구현 (C++)
| |
구현 (Python)
| |
코너 케이스 체크리스트
q = 1이면 모든 항이 0 → 합 0n < q인 작은 접두부만 있는 경우gcd(p, q) > 1로 주기 길이가 짧아지는 경우- 최대 경계:
p = q = n = 10^6,W = 10^5(케이스당O(log q)보장 필요) - 오버플로: C++에서는 중간 계산에 128-bit 사용, Python은 빅인트 안전
제출 전 점검
- 표준 입출력, 개행 형식 확인
- C++: 64-bit 범위 점검, 곱셈 중간값 i128 캐스팅 적용
floor_sum인덱스 범위 정의(0..n-1)와 사용처 일치 확인
참고자료
- 잔여류 주기성과
sum of floors고전 테크닉 - AtCoder Library
floor_sum아이디어
![Featured image of post [Algorithm] cpp-python 백준 16998번: Mod World](/post/algorithm/2025-08-14-boj-16998-its-a-mod-mod-mod-mod-world-cpp-python-solution/wordcloud_hu_a8e1779dde1cb840.png)
![[Algorithm] cpp-python 백준 16404번: 주식회사 승범이네 - 서브트리 갱신·점 질의](/post/algorithm/2025-08-14-boj-16404-seungbeom-company-subtree-range-add-point-query-cpp-solution/wordcloud_hu_b5cdadbbe291c392.png)
![[Algorithm] cpp-python 백준 16901번: XOR MST](/post/algorithm/2025-08-14-boj-16901-xor-mst-cpp-python-solution/wordcloud_hu_5a04de18b2ccb6ef.png)
![[Algorithm] cpp-python 백준 16998번: Mod World](/post/algorithm/2025-08-14-boj-16998-its-a-mod-mod-mod-mod-world-cpp-python-solution/wordcloud_hu_fba4e1ec85a891a0.png)
![[Algorithm] cpp-python 백준 17169번: Eat Economically - 그리디 O(N log N)](/post/algorithm/2025-08-14-boj-17169-eat-economically-cpp-python-solution/wordcloud_hu_7cec762deb585327.png)
![[Algorithm] cpp-python 백준 17399번: 트리의 외심](/post/algorithm/2025-08-14-boj-17399-tree-circumcenter-lca-binary-lifting-cpp-python-solution/wordcloud_hu_87955e535f66158b.png)
![[Algorithm] cpp 백준 17134번: 르모앙의 추측](/post/algorithm/2025-08-14-boj-17134-lemoine-conjecture-fft-convolution/wordcloud_hu_537c64ece4c0888d.png)
![[Algorithm] cpp-python 백준 10854번: Divisions - 약수 개수](/post/algorithm/2025-09-16-boj-10854-divisions-number-of-divisors-cpp-python-solution/wordcloud_hu_f2a53254f2f126b2.png)
![[Algorithm] cpp-python 백준 11479번: 서로 다른 부분 문자열의 개수](/post/algorithm/2025-08-14-boj-11479-distinct-substrings-suffix-array-sam/wordcloud_hu_68ec78856e891392.png)
![[Algorithm] cpp-python 백준 12735번: Boat](/post/algorithm/2025-08-14-boj-12735-boat-cpp-python-solution/wordcloud_hu_dfad17dd2b1a530d.png)
![[Algorithm] cpp-python 백준 13034번: 다각형 게임 - Sprague–Grundy DP](/post/algorithm/2025-08-14-boj-13034-polygon-game-cpp-python-solution/wordcloud_hu_68da3af9ccf59d0a.png)