Problem Solving
알고리즘 :: 백준 :: 1629 - 곱셈
soreDemo
2025. 1. 7. 12:28
❔문제
🔄 문제 및 입출력 조건 파악
입력: 2,147,483,647 이하의 자연수 A, B, C
출력: A를 B번 곱한 수를 C로 나눈 나머지 ($A^B \pmod C$)
✏️ 문제풀이
첫 시도 :: 잘못된 접근
나누는 수에 따라 나머지가 반복되는 성질이 있는 일부 수가 있는데, 모든 수가 그럴 것이라 생각하고 풀었습니다.
그러나, 반드시 모든 수가 나머지가 반복되진 않습니다.
ull A, B, C, result = 1;
cin >> A >> B >> C;
set<ull> s;
while (B--) {
result = (result * A) % C;
if (s.find(result) != s.end()) break;
s.insert(result);
}
cout << *next(begin(s), B % s.size()) << '\n';
- `std::set<int>`을 이용해서, 나머지가 반복될 때 멈춥니다.
- 예제입력의 `10 11 12`의 경우, 나머지가 10 -> 4 -> 4 -> 4 -> 4... 로 4가 반복되니
- 반복을 발견하면 바로 O(1)에 나머지를 구할 수 있지 않나? 라는 생각을 했습니다.
올바른 풀이
A, B, C 모두 범위가 약 21억이고 A^B를 C로 나눈 나머지를 구해야 하므로,
- 중간 계산과정에서 `int` 범위를 넘을 수 있다는 점,
- $O(n)$으로 풀면 시간초과가 난다는 점을 파악해야 합니다.
핵심 아이디어는 분할정복입니다.
- $(A^B)\ \% \ C$는 $(A^{B/2} \times A^{B/2}))\ \% \ C$와 같으며
- $(A^{B/2})\ \% \ C$는 $(A^{B/4} \times A^{B/4})\ \% \ C$와 같으며
- … (반복) ....
이 아이디어를 적용하면, 즉, $O(log_2N)$으로 해결할 수 있습니다.
B를 나눌 때, 홀수일 경우에는 B를 한 번 더 곱해서 보상해줘야 한다는 점을 잊지 맙시다.
(e.g. 5 = 2 + 2 ( + 1 보상해줘야 함 ))
📝 코드
#include <bits/stdc++.h>
using namespace std;
using ull = unsigned long long;
// O(n) -> O(log₂N)
ull solve(ull A, ull B, ull C) {
if (B == 1) return A % C;
ull ret = solve(A, B / 2, C); // get A^(n/2)
ret = (ret * ret) % C; // A^n = A^(n/2) * A^(n/2)
if (B & 1) ret = (ret * A) % C; // compensate one if B is odd.
return ret;
}
int main() {
ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
ull A, B, C;
cin >> A >> B >> C;
cout << solve(A, B, C) << '\n';
}
🕧 결과