刷到一道挺好的题目,复习了很多知识点,在此整理一下
解法参考:https://blog.csdn.net/github_39329077/article/details/86708747
题目
2991:2011
总时间限制: 1000ms 内存限制: 65536kB
描述
已知长度最大为200位的正整数n,请求出2011n的后四位。
输入
第一行为一个正整数k,代表有k组数据,k<=200接下来的k行,每行都有一个正整数n,n的位数<=200
输出
每一个n的结果为一个整数占一行,若不足4位,去除高位多余的0
样例输入
3
5
28
792
样例输出
1051
81
5521
模运算的性质
之后的几个解法都要用到其中一些性质
模运算与基本四则运算有些相似,但是除法例外。其规则如下:
(a + b) % p = (a % p + b % p) % p (1)
(a - b) % p = (a % p - b % p ) % p (2)
(a * b) % p = (a % p * b % p) % p (3)
(ab) % p = ((a % p)b) % p (4)
结合律:
((a+b) % p + c) % p = (a + (b+c) % p) % p (5)
((a*b) % p * c)% p = (a * (b * c)%p) % p (6)
交换律:
(a + b) % p = (b+a) % p (7)
(a * b) % p = (b * a) % p (8)
分配律:
((a +b)% p * c) % p = ((a * c) % p + (b * c) % p) % p (9)
重要定理:
若a≡b (% p),则对于任意的c,都有(a + c) ≡ (b + c) (%p);(10)
若a≡b (% p),则对于任意的正整数c,都有(a * c) ≡ (b * c) (%p);(11)
若a≡b (% p),c≡d (% p),则 (a + c) ≡ (b + d) (%p),(a - c) ≡ (b - d) (%p),
(a * c) ≡ (b * d) (%p); (12)
解法
解法一
打表找到2011n模10000的结果的规律,发现500种一循环
同时利用模运算的性质,例子
9876 % n
= (9 * 103 + 8 * 102 + 7 * 101 + 6 * 100 )%n
= (((9 * 10 + 8) * 10 + 7) * 10 + 6)%n
=((((9%n) * 10 + 8)%n * 10 + 7)%n * 10 + 6)%n
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
char ch[202]; //字符串形式存n
int a[502]; //存2011的n次方余10000的所有情况,但数组开的大小,有待探讨。
int b[202]; //将n每位化成整数存下
int main()
{
int n = 0, temp = 2011;
//找到2011^n的后四位的所有情况的个数,退出循环时n为500
do{
a[++n] = temp;
temp = temp * 2011 % 10000;
}while(temp != 2011);
a[0] = a[n]; //此处赋值给a[0]与后面幂取模的值对应。
int t, k, x, len; //k是之后遍历b数组时的下标
scanf("%d", &t);
for(int i = 0; i < t; i++)
{
x = 0, k = 0;
memset(b, 0, sizeof(b));
scanf("%s", ch);
len = strlen(ch);
for(int j = 0; j < len; ++j)
b[j] = ch[j] - '0';
while(k < len) //n为2011^n后四位所有情况的个数,此处对高精度幂模n
x = (x * 10 + b[k++]) % n; //得到大数对n取余结果
printf("%d\n", a[x]);
}
}
解法二
这个解法直接取输入的幂的低四位来作计算,为什么呢?
编个循环代码跑了一下发现201110000%10000=1!
我们可以令 n = k * 10000+r(k,r都是整数),那么我们想要求的2011n的后四位可以表示为
(2011n)%10000
= (2011k*10000+r )%10000
= (2011k*10000 * 2011r)%10000
= (201110000 % 10000)k * (2011r %10000)
= 1 * 2011r %10000
= 2011r %10000
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
char ch[202];
int main()
{
int t;
scanf("%d", &t);
for(int i = 0; i < t; ++i)
{
scanf("%s", ch);
int len = strlen(ch);
int z= 0;
//z为幂,这里直接取输入的幂的低四位
for(int j = len-4; j < len; ++j)
if(j >= 0)
z = z*10 + ch[j] - '0';
int p = 1, temp = 2011;
// 类似快速幂,但是是先算完小于z的最大2^k部分然后再枚举计算后面部分(如z == 18, 先算到2011的2^4幂即2011^16, 后面再逐个*2011至2011^18)
while(p * 2 <= z)
{
temp = temp * temp % 10000;
p *= 2;
}
for(int i = p+1; i <= z; ++i)
temp = temp * 2011 % 10000;
printf("%d\n", temp);
// int temp = 2011 % 10000, ans = 1; // 快速幂写法
// while(z)
// {
// if(z & 1)
// ans = ans * temp % 10000;
// temp = temp * temp % 10000;
// z >>= 1;
// }
// printf("%d\n", ans);
}
return 0;
}
解法三 快速幂+高精度
这个最基础,但是也适合最一般的情况
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
char ch[202];
int b[202];
/*
高精度除低精度
*/
void devide()
{
int i = b[0];
while(i)
{
if(i > 1)
b[i-1] += (b[i]%2)*10;
b[i] /= 2;
i--;
}
i = b[0];
while(b[i] == 0)//最初while循环里加了b[0]--;导致出不了循环。
i--;
//注意while条件里加i--,最后一次判断无论是否为1,i均减1。
if(i == 0)
b[0] = 1;
else
b[0] = i;
}
int main()
{
int t;
scanf("%d", &t);
for(int i = 0; i < t; i++)
{
int temp = 2011, ans = 1;
scanf("%s", ch);
b[0] = strlen(ch);
for(int i = 1; i <= b[0]; i++)
b[i] = ch[b[0]-i] - '0';
//快速幂,(b[0] == 1 && b[1] == 0)均满足时表示幂为零。
while(!(b[0] == 1 && b[1] == 0))
{
if(b[1]%2 == 1)
ans = (ans * temp) % 10000;
temp = (temp * temp) % 10000;
devide();
}
printf("%d\n", ans);
}
return 0;
}
快速幂模板
求 ab ,返回值类型根据题目来,有时可用int ,有时用int就不够,需要long long
讲解可看https://www.cnblogs.com/CXCXCXC/p/4641812.html
版本一:位运算
11的二进制是1101(b),我们将 a 11 a^{11} a11转化为算 a 2 0 ∗ a 2 1 ∗ a 2 3 a^{2^0}*a^{2^1}*a^{2^3} a20∗a21∗a23 ,也就是 a 1 ∗ a 2 ∗ a 8 a^1 * a^2 * a^8 a1∗a2∗a8
long long power(int a, int b){
long long res = 1, base = a;
while(b>0){
if(b&1) //若当前b的最低位为1
res *= base;
base *= base;
b >>= 1;
}
return res;
}
版本二:分治法
任意一个自然数n,可以n = 2 * n/2 + n%2,如19 = 2 * 9 + 1,
即a19 = a2*9+1 = (a9)2 * a,同时9可以继续拆分下去便得最终解。
int power(int a, int b){
if(b==0) return 1;
if(b==1) return a;
int res = power(a,b/2);
res *= res;
return res * power(a,b%2);
}