https://www.acmicpc.net/problem/27299
27299번: 헌내기 현철
불과 몇 달 전만 해도 새내기였던 현철이는 $22$학번을 이십이학번이 아닌 이이학번으로 읽는다는 것을 신기하게 생각했다. 그런데, 이제 $23$학번이 들어온다는 소식에 현철이는 큰 충격에 빠졌
www.acmicpc.net
구해야 하는 값은 $A^{B^{C}}$를 $10^{7}$으로 나눈 나머지입니다. 여기까지는 생각하기 매우 쉽지만, $C$의 범위가 매우 큽니다.
Euler's Theorem을 반복하여 적용할 수 있지만, 저는 그냥 한 번만 적용하고 끝내는 게 더 편할 수도 있다는 생각을 했습니다. 왜냐하면 17315번 에서 이미 한 번 구현했기 때문입니다 ㅎㅎ..
주의할 점은 엣지 케이스인데,
$$A^{B^{C}} \equiv A^{ B^{C} \; \textrm{mod} \; 10^{7} + \phi (10^{7}) } \; \textrm{mod} \; 10^{7}$$
가
$$B^{C} \geq \log_{2} (10^{7}) \approx 24$$
에서 성립한다는 것입니다. 저는 안전하게 $B^{C}$가 27 이상일 때 Euler's Theorem을 쓰기로 했습니다.
따라서 제 코드는 다음과 같습니다.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>
typedef unsigned long long ull;
#define mod 10000000ull
#define phimod 4000000ull
bool is_B_to_C_small_than_27 (ull B, char* C, int L);
ull power (ull x, ull y);
ull power_modular_mod (ull x, ull y);
ull power_modular_phimod (ull x, ull y);
ull power_modular_phimod_log10 (ull x, char* P, int L);
int main() {
int T;
scanf("%d", &T);
while(T--) {
ull A, B, i;
char C[1001];
scanf("%llu %llu %llu %s", &A, &B, &i, C);
if (B == 1) {
printf("%llu\n", A % mod);
}
else {
int lenC = strlen(C);
if (is_B_to_C_small_than_27(B, C, lenC)) {
ull B_to_the_power_of_C = power(B, atoi(C));
ull remainder = power_modular_mod (A, B_to_the_power_of_C);
printf("%lld\n", (remainder / power(10, i)) % 10);
}
else {
ull B_to_the_power_of_C_phimod = power_modular_phimod_log10(B, C, lenC);
ull remainder = power_modular_mod (A, B_to_the_power_of_C_phimod + phimod);
printf("%lld\n", (remainder / power(10, i)) % 10);
}
}
}
return 0;
}
bool is_B_to_C_small_than_27 (ull B, char* C, int L) {
// return true if B^C < 27
if (B >= 27 || L >= 2) {
return false;
}
else if (B >= 6 && C[0] >= '2') {
return false;
}
else if (B >= 3 && C[0] >= '3') {
return false;
}
else if (B >= 2 && C[0] >= '5') {
return false;
}
else {
return true;
}
}
ull power (ull x, ull y) {
ull result = 1;
while (y) {
if (y % 2) {
result = result * x;
}
x = x * x;
y /= 2;
}
return result;
}
ull power_modular_mod (ull x, ull y) {
ull result = 1;
while (y) {
if (y % 2) {
result = result * x % mod;
}
x = x * x % mod;
y /= 2;
}
return result;
}
ull power_modular_phimod (ull x, ull y) {
ull result = 1;
while (y) {
if (y % 2) {
result = result * x % phimod;
}
x = x * x % phimod;
y /= 2;
}
return result;
}
ull power_modular_phimod_log10 (ull x, char* P, int L) {
ull result = 1;
for (int i = L - 1; i >= 0; i--) {
result = result * power_modular_phimod(x, P[i] - '1' + 1) % phimod;
x = power_modular_phimod(x, 10);
}
return result;
}
(C11, 12ms, 1116KB, 제출번호 55100051)
제가 사용한 $10^{7}$은 소인수가 2개밖에 없는 수이므로, chinese remainder theorem을 적용하기도 매우 수월합니다. 다만 CRT를 적용했을 때 어느정도 더 빨라질 지는 잘 모르겠습니다.
제일 시간을 줄이기 좋은 방법은 $B^{C}$에서 한 번 더 Euler's Theorem을 적용하는 것 같은데, 여기까진 아직 해보지 않았습니다.
'분할 정복을 이용한 거듭제곱 > 정수 거듭제곱' 카테고리의 다른 글
백준 18151번: DivModulo (0) | 2023.02.28 |
---|---|
백준 10909번: Quaternion inverse (0) | 2023.02.14 |
백준 14679번: 영우와 '갓4' (0) | 2023.02.12 |
백준 9556번: 수학 숙제 (0) | 2023.01.28 |
백준 16201번: 발코니 공사 (1) | 2023.01.26 |