前言
参考视频教程 洛谷 普及组试炼场 - 高精度算法
理解高精度算法,当题目数据规模过大时可能就需要考虑高精度解决了,高精度一般是通过数组输出~
以简单基础普及题型为例,可在洛谷上查找题目提交,代码仅供参考。
题目列表:
1.P1601 A+B Problem
2.P2142 高精度减法
3.P1303 A*B Problem
4.P1255 数楼梯
5.P1604 B进制星球
6.P1045 麦森数
7.算法提高 快速幂+取模
1.P1601 A+B Problem(高精)
题目描述
高精度加法,相当于a+b problem,不用考虑负数.
输入格式
分两行输入。a,b \leq 10^{500}a,b≤10500
输出格式
输出只有一行,代表a+ba+b的值
输入输出样例
输入 #1复制
1
1
输出 #1复制
2
输入 #2复制
1001
9099
输出 #2复制
10100
代码
#include <iostream>
#include <string>
using namespace std;
string aa,bb; //可能超过long long 得string输入
int a[10000010],b[10000010],c[10000010],la,lb,lc;
int main()
{
cin>>aa>>bb;
la = aa.length();
lb = bb.length();
for(int i=aa.length()-1;i>=0;i--)
{
a[aa.length()-i] = aa[i] - '0';
}
for(int i=bb.length()-1;i>=0;i--)
{
b[bb.length()-i] = bb[i] - '0';
}
lc = max(la,lb);
for(int i = 1 ; i <= lc; i++)
{
c[i] += a[i] +b[i]; //必须是 += , 防止漏了进位的数
c[i+1] = c[i]/10; //是否进位
c[i]%=10; //取余
}
if(c[lc+1]>0) //这里是判断是否最高位进位
lc++;
for(int i=lc;i>=1;i--)
{
cout<<c[i];
}
}
2.P2142 高精度减法
题目描述
高精度减法。
输入格式
两个整数 a,ba,b(第二个可能比第一个大)。
输出格式
结果(是负数要输出负号)。
输入输出样例
输入 #1复制
2
1
输出 #1复制
1
代码
#include <iostream>
#include <string>
#include <bits/stdc++.h>
using namespace std;
string aa, bb; //可能超过long long 得string输入
int a[10000010], b[10000010], c[10000010], la, lb, lc;
int main()
{
cin >> aa >> bb;
la = aa.length();
lb = bb.length();
if (la < lb || (la == lb && aa < bb)) //判断 aa,bb 的大小,当长度一致时string可以判断大小
{
swap(aa, bb);
swap(la, lb);
cout << "-";
}
for (int i = aa.length() - 1; i >= 0; i--)
{
a[aa.length() - i] = aa[i] - '0';
}
for (int i = bb.length() - 1; i >= 0; i--)
{
b[bb.length() - i] = bb[i] - '0';
}
for (int i = 1; i <= la; i++)
{
if (a[i] < b[i]) //当当前位为小于时
{
a[i] += 10;
a[i + 1]--;
}
c[i] = a[i] - b[i];
}
while (c[la] == 0 && la > 1) //判断前位是否为 0
la--;
for (int i = la; i >= 1; i--)
{
cout << c[i];
}
}
3.P1303 A*B Problem
题目描述
求两数的积。
输入格式
两行,两个整数。
输出格式
一行一个整数表示乘积。
输入输出样例
输入 #1复制
1
2
输出 #1复制
2
说明/提示
每个数字不超过 10^{2000}102000 ,需用高精。
代码
#include <iostream>
#include <string>
#include <bits/stdc++.h>
using namespace std;
string aa, bb; //可能超过long long 得string输入
int a[10000010], b[10000010], c[10000010], la, lb, lc;
int main()
{
cin >> aa >> bb;
la = aa.length();
lb = bb.length();
if (aa[0] == '-' && bb[0] != '-' || bb[0] == '-' && aa[0] != '-') //貌似不需要
{
cout << "-";
}
for (int i = aa.length() - 1; i >= 0; i--)
{
a[aa.length() - i] = aa[i] - '0';
}
for (int i = bb.length() - 1; i >= 0; i--)
{
b[bb.length() - i] = bb[i] - '0';
}
for (int i = 1; i <= la; i++)
{
for (int j = 1; j <= lb; j++)
{
c[i + j - 1] += a[i] * b[j];
c[i + j] += c[i + j - 1] / 10;
c[i + j - 1] %= 10;
}
}
lc = la + lb;
while (c[lc] == 0 && lc > 1) //判断前位是否为 0
lc--;
for (int i = lc; i >= 1; i--)
{
cout << c[i];
}
}
4.P1255 数楼梯
题目描述
楼梯有 N 阶,上楼可以一步上一阶,也可以一步上二阶。
编一个程序,计算共有多少种不同的走法。
输入格式
一个数字,楼梯数。
输出格式
输出走的方式总数。
输入输出样例
输入 #1复制
4
输出 #1复制
5
说明/提示
- 对于 60%60% 的数据,N*≤50;
- 对于 100%100% 的数据,N≤5000。
代码
#include<iostream>
#include<bits/stdc++.h>
using namespace std;
int a[500050], b[500050], c[500050], lc = 1; //lc 为位数
int main() //好好想想题目描述,会发现跟斐波那契数列差不多,看数据规模会发现可能需要用到高精度解决的方法
{
int n;
cin >> n;
// int a = 1,b=2,c; //下面方法的前身,当不用数组时的斐波那契数列可以这样表示
// for(int i=3;i<=n;i++)
// {
// c=a+b;
// a=b;
// b=c;
// }
// cout<<c;
a[1] = 1;
b[1] = 2;
for (int i = 3; i <= n; i++)
{
memset(c, 0, sizeof(c)); //每次都需要对 c 重新清零
for (int j = 1; j <= lc; j++)
{
c[j] += a[j] + b[j];
c[j + 1] += c[j] / 10;
c[j] = c[j] % 10;
}
if (c[lc + 1] > 0)
lc++;
for (int j = 1; j <= lc; j++)
{
a[j] = b[j];
}
for (int j = 1; j <= lc; j++)
{
b[j] = c[j];
}
}
if (n >= 3)
{
for (int i = lc; i >= 1; i--)
{
cout << c[i];
}
}
else if (n < 3)
{
switch (n)
{
case 0:cout << "0"; break; //别忘了 break !!
case 1:cout << "1"; break;
case 2:cout << "2"; break;
}
}
return 0;
}
5.P1604 B进制星球
题目背景
进制题目,而且还是个计算器~~
题目描述
话说有一天,小Z乘坐宇宙飞船,飞到一个美丽的星球。因为历史的原因,科技在这个美丽的星球上并不很发达,星球上人们普遍采用B(2<=B<=36)进制计数。星球上的人们用美味的食物招待了小Z,作为回报,小Z希望送一个能够完成B进制加法的计算器给他们。 现在小Z希望你可以帮助他,编写实现B进制加法的程序。
输入格式
共3行第1行:一个十进制的整数,表示进制B。第2-3行:每行一个B进制数正整数。数字的每一位属于{0,1,2,3,4,5,6,7,8,9,A,B……},每个数字长度<=2000位。
输出格式
一个B进制数,表示输入的两个数的和。
输入输出样例
输入 #1复制
4
123
321
输出 #1复制
1110
代码
#include <iostream>
#include <string>
using namespace std;
string aa, bb;
int a[300020], b[300020], c[300020], la, lb, lc = 1;
int main()
{
int n;
cin >> n;
cin >> aa >> bb;
la = aa.length();
lb = bb.length();
for (int i = la - 1; i >= 0; i--)
{
if (aa[i] >= 'A')
{
a[la - i] = aa[i] - 'A' + 10; //需要注意大于10进制的情况
}
else
{
a[la - i] = aa[i] - '0';
}
}
for (int i = lb - 1; i >= 0; i--)
{
if (bb[i] >= 'A')
{
b[lb - i] = bb[i] - 'A' + 10;
}
else
{
b[lb - i] = bb[i] - '0';
}
}
lc = max(la, lb);
for (int i = 1; i <= lc; i++)
{
c[i] += a[i] + b[i];
c[i + 1] = c[i] / n;
c[i] = c[i] % n;
}
if (c[lc + 1] > 0)
lc++;
for (int i = lc; i >= 1; i--)
{
if (c[i] >= 10)
{
cout << char(c[i] + 'A' - 10);
}
else
{
cout << c[i];
}
}
return 0;
}
6.P1045 麦森数
题目描述
形如2P-1的素数称为麦森数,这时P*P*一定也是个素数。但反过来不一定,即如果P*P*是个素数,2P-1不一定也是素数。到1998年底,人们已找到了37个麦森数。最大的一个是P=3021377,它有909526位。麦森数有许多重要应用,它与完全数密切相关。
任务:从文件中输入P(1000<P<3100000),计算2^P-1的位数和最后500位数字(用十进制高精度数表示)
输入格式
文件中只包含一个整数P(1000<P<3100000)
输出格式
第一行:十进制高精度数2^P-1的位数。
第2-11行:十进制高精度数2^P-1的最后500位数字。(每行输出50位,共输出10行,不足500位时高位补0)
不必验证2^P-1与P是否为素数。
输入输出样例
输入 #1复制
1279
输出 #1
386
00000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000
00000000000000104079321946643990819252403273640855
38615262247266704805319112350403608059673360298012
23944173232418484242161395428100779138356624832346
49081399066056773207629241295093892203457731833496
61583550472959420547689811211693677147548478866962
50138443826029173234888531116082853841658502825560
46662248318909188018470682222031405210266984354887
32958028878050869736186900714720710555703168729087
代码
#include <iostream>
#include <bits/stdc++.h>
using namespace std;
int m[1010];
int a[1010];
int temp[1010]; //要有个临时保存的
void xianchen()
{
memset(temp, 0, sizeof(temp));
for (int i = 1; i <= 500; i++) //直接500给他
{
for (int j = 1; j <= 500; j++)
{
temp[i + j - 1] += m[i] * a[j];
temp[i + j] += temp[i + j - 1] / 10;
temp[i + j - 1] %= 10;
}
}
memcpy(m, temp, sizeof(m)); //把 temp 的值传给 m
}
void zichen()
{
memset(temp, 0, sizeof(temp));
for (int i = 1; i <= 500; i++)
{
for (int j = 1; j <= 500; j++)
{
temp[i + j - 1] += a[i] * a[j];
temp[i + j] += temp[i + j - 1] / 10;
temp[i + j - 1] %= 10;
}
}
memcpy(a, temp, sizeof(a));
}
int main()
{
long long p;
cin >> p;
cout << int((p * log10(2)) + 1) << endl; //求位数可以通过 log10()+1 解决
a[1] = 2;
m[1] = 1;
while (p != 0)
{
if (p % 2 == 1)
{
xianchen();
}
p /= 2;
zichen();
}
m[1]--;
for (int i = 500; i >= 1; i--)
{
if (i % 50 == 0 && i != 500)
{
cout << endl;
}
cout << m[i];
}
}
7.算法提高 快速幂+取模
问题描述
给定A, B, P,求(A^B) mod P。
输入格式
输入共一行。
第一行有三个数,N, M, P。
输出格式
输出共一行,表示所求。
样例输入
2 5 3
样例输出
2
数据规模和约定
共10组数据
对100%的数据,A, B为long long范围内的非负整数,P为int内的非负整数。
代码
参考 https://www.bilibili.com/video/BV12r4y1w7tx?from=search&seid=7396560337832777912
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QU3068yC-1618067554342)(C:\Users\HP\AppData\Roaming\Typora\typora-user-images\image-20210410154043709.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bMEDSQDQ-1618067554346)(C:\Users\HP\AppData\Roaming\Typora\typora-user-images\image-20210410154051135.png)]
#include<iostream>
using namespace std;
int main()
{
long long a, b;
int p;
cin >> a >> b >> p;
long long ans = 1;
a %= p;
while (b != 0)
{
if (b & 1) //即 b%2==1 ,可以加快速度
{
ans = (ans * a) % p;
}
a = (a * a) % p;
b >>= 1; // 即 b /= 2;
}
cout << ans << endl;
}