FOJ 1752 && FOJ 1759 (a^b%c 的不同情况)
博客原文地址:http://blog.csdn.net/xuechelingxiao/article/details/38614491
对于一般的求a^b%c的值,当a,b都在long long范围内,c在1e9的时候,都可以用快速幂取模进行求解。
1 LL powerMod(LL x, LL k, LL m){ 2 LL res = 1; 3 while(x %= m, k){ 4 if(k&1) res *= x, res %= m; 5 x *= x, k >>=1; 6 } 7 return res; 8 }
但是当其中的参数变得相对大之后,单纯的快速幂可能就不能解决问题了,下面这两个题就是当a^b%c中某个参数变大之后出现的问题。
题目大意:题意很简单,就是求a^b%c的结果,只是a,b,c的范围是 (1<=a,b,c<2^63)。
思路:这个题最关键的问题是在快速幂的时候,由于a的最大值是2^63-1,所以会出现乘法溢出,这也是最容易WA的地方。
要解决这个问题,就需要将乘法转换成加法,进行“快速乘”(暂且这么叫吧,我也不知道叫啥-。-),就是代码中的multiplyMod,接下来就没什么问题了。
1 #include <stdio.h> 2 #include <iostream> 3 #define ULL long long 4 using namespace std; 5 6 ULL n, m, mod; 7 8 ULL multiplyMod(ULL x, ULL k, ULL m){ 9 ULL res = 0; 10 x %= m; 11 while(k){ 12 if(k&1){ 13 res += x; 14 if(res >= m) 15 res -= m; 16 } 17 x += x; 18 if(x >= m){ 19 x -= m; 20 } 21 k >>=1; 22 } 23 return res; 24 } 25 26 ULL powerMod(ULL x, ULL k, ULL m){ 27 ULL res = 1; 28 while(k){ 29 if(k&1){ 30 res = multiplyMod(res, x, m); 31 } 32 x = multiplyMod(x, x, m), k >>=1; 33 } 34 return res; 35 } 36 37 int main() 38 { 39 while(~scanf("%I64d%I64d%I64d", &n, &m, &mod)){ 40 printf("%I64d\n", powerMod(n, m, mod)); 41 } 42 43 return 0; 44 }
题目大意:一样的求a^b%c的结果,只是b的范围变得很大了, (1<=A,C<=1000000000,1<=B<=10^1000000)。
思路:对于这个问题,就需要用到一个公式
A^x = A^(x % Phi(C) + Phi(C)) (mod C)
对于这个公式,可以给出两个证明的博客,一个是aekdycoin大犇的证明:http://www.narutoacm.com/archives/a-pow-b-mod-m/
另一个是 http://www.narutoacm.com/archives/a-pow-b-mod-m/
有了理论支持,这个题也就不是什么问题了,代码如下。
1 #include <stdio.h> 2 #include <string.h> 3 #define LL long long 4 5 int eular(int n){ 6 int ret = 1; 7 for(int i = 2; i*i <= n;i++) 8 if(n%i == 0){ 9 n /= i, ret *= i-1; 10 while(n%i == 0) 11 n /= i, ret *= i; 12 } 13 if(n > 1) 14 ret *= n-1; 15 return ret; 16 } 17 18 LL powerMod(LL x, LL k, LL m){ 19 LL res = 1; 20 while(x %= m, k){ 21 if(k&1) res *= x, res %= m; 22 x *= x, k >>=1; 23 } 24 return res; 25 } 26 27 LL n, mod; 28 char m[1000005]; 29 30 int main() 31 { 32 while(~scanf("%I64d%s%I64d", &n, m, &mod)){ 33 LL phi = eular(mod); 34 int len = strlen(m); 35 LL num = 0; 36 for(int i = 0; i < len; ++i){ 37 if(len >= 10){ 38 num = (num*10+(m[i]-'0'))%phi + phi; 39 } 40 else { 41 num = num*10+(m[i]-'0'); 42 } 43 } 44 printf("%I64d\n", powerMod(n, num, mod)); 45 } 46 47 return 0; 48 }