高精度算法普遍日渐落后于时代
Python 和 Java 都支持无限精度整数
高精度算法的考查几乎只能在中国看到
int
, long long
类型都是有范围的,万一需要处理更大范围的数字怎么办呢?
int <= 2e9 == 2**31 - 1
long long <= 9e18 == 2**63 - 1
只能手动模拟,或者使用其他语言(Python 或 Java)。
用数组存,先存低位,再存高位,比如a[0]
存个位,a[1]
存十位。
a[i]
对应的系数是,计算起来比较方便。
整个数字有多少位,可以不存,即认为处理的数字高位有;
也可以存下来,并且在运算中维护,优化运算效率。
有时算作一位数,有时算作零位数,视情况而定。
先把整个数字读入,然后翻转,每个位置减去字符'0'
。
这样的写法可以方便的进行压位操作。
先确定位数
输出最高位(同时处理0的情况)
从高位到低位依次输出(如果用到压位,需处理前导0的情况)
直接模拟即可,最后扫一遍,处理进位。
建议把运算和处理进位借位用两个循环实现。
位数加位数,结果可能有 或 位。
先比较位数,如果位数相等,从高位到低位依次比较。
也可以不考虑位数,直接从高位到低位依次比较(不存在的位认为是0)
只允许大的减去小的
直接模拟即可,最后扫一遍,处理借位。
建议把运算和处理借位用两个循环实现。
最后要去掉前导零,特别注意结果为0的情况
直接模拟即可,最后扫一遍,处理进位。
位数乘以位数,结果可能有 或 位。
还有可能其中一个乘数是 。
可以用FFT优化。但是优化很小,因为高精度乘法可以压位优化,
但是FFT后就不能压位了。
高精度对一个范围比较小的数字的取模是一个比较简单的问题
扫一遍模拟即可
不能使用笔算的试除法
一般使用二分即可
将一个位的高精度的数字从进制转化为进制。
如果存在整数和使得那么可以将进制的数字,每位一组,一起转化为位的进制,时间复杂度是的。
如果不满足以上条件,那么就只能每次模除以依次获得进制下的每一位数字了。
一些高精度语言,比如Ruby用了更快的算法(分治)
往往通过乘以10的次幂的形式转化为整数
进制的范围,只要不太大(比如不超过int
类型)计算的复杂度是差不多的。
所以相对于十进制来说,更加常用的是万进制,甚至是进制,这样可以加快计算。
int
范围高达2e9
,所以可以使用万进制或者亿进制存储,输出时需要特殊处理前导0
和0
的情况。
因为Java, Python等语言均支持高精度,高精度在在线算法比赛出现的概率并不高。
当然也有一些比谁手写高精度快的题目,比如算高精度Pi。
Java 可以用 nextProbablePrime 来找到 >n
最小的质数
https://www.luogu.com.cn/problem/P1601
高精度加法,相当于a+b problem,不用考虑负数.
分两行输入。
输出只有一行,代表的值
1
1
2
1001
9099
10100
#include <bits/stdc++.h> using namespace std; char a[1020]; char b[1020]; char c[1020]; int main() { scanf("%s%s", a, b); int la = strlen(a); int lb = strlen(b); reverse(a, a + la); reverse(b, b + lb); for (int i = 0; i < la; i++) { a[i] -= '0'; } for (int i = 0; i < lb; i++) { b[i] -= '0'; } int lc = max(la, lb); for (int i = 0; i < lc; i++) { c[i] = a[i] + b[i]; } for (int i = 0; i < lc; i++) { while (c[i] >= 10) { c[i] -= 10; c[i + 1] += 1; } } if (c[lc] > 0) { lc++; } for (int i = lc - 1; i >= 0; i--) { printf("%d", c[i]); } printf("\n"); return 0; }
高精度加法
https://www.luogu.com.cn/problem/P2142
高精度减法。
两个整数 (第二个可能比第一个大)。
结果(是负数要输出负号)。
2
1
1
#include <bits/stdc++.h> using namespace std; string A, B; int a[10020]; int b[10020]; bool cmp(const string &A, const string &B) { if (A.size() != B.size()) { return A.size() < B.size(); } return A < B; } int main() { cin >> A >> B; if (cmp(A, B)) { swap(A, B); printf("-"); } for (int i = 0; i < A.size(); i++) { a[i] = A[A.size() - i - 1] - '0'; } for (int i = 0; i < B.size(); i++) { b[i] = B[B.size() - i - 1] - '0'; } for (int i = 0; i < B.size(); i++) { a[i] -= b[i]; } for (int i = 0; i < A.size(); i++) { while (a[i] < 0) { a[i] += 10; a[i + 1]--; } } int l = A.size(); while (l > 0 && a[l] == 0) { l--; } for (int i = l; i >= 0; i--) { printf("%d", a[i]); } printf("\n"); return 0; }
高精度减法
https://www.luogu.com.cn/problem/P1303
求两数的积。
两行,两个整数。
一行一个整数表示乘积。
1
2
2
每个数字不超过 ,需用高精。
#include <bits/stdc++.h> using namespace std; char s[4020]; int a[4020]; int b[4020]; int c[4020]; int main() { scanf("%s", s); int la = strlen(s); for (int i = 0; i < la; i++) { a[i] = s[i] - '0'; } reverse(a, a + la); scanf("%s", s); int lb = strlen(s); for (int i = 0; i < lb; i++) { b[i] = s[i] - '0'; } reverse(b, b + lb); int lc = la + lb; for (int i = 0; i < la; i++) { for (int j = 0; j < lb; j++) { c[i + j] += a[i] * b[j]; } } for (int i = 0; i < lc; i++) { c[i + 1] += c[i] / 10; c[i] %= 10; } while (lc > 1 && c[lc - 1] == 0) { lc--; } for (int i = lc - 1; i >= 0; i--) { printf("%d", c[i]); } printf("\n"); return 0; }
高精度乘法
https://www.luogu.com.cn/problem/P1480
输入两个整数 ,输出它们的商。
两行,第一行是被除数,第二行是除数。
一行,商的整数部分。
10
2
5
,。
#include <bits/stdc++.h> using namespace std; char s[5020]; int a[5020]; int b; int main() { scanf("%s%d", s, &b); int n = strlen(s); for (int i = 0; i < n; i++) { a[i] = s[n - i - 1] - '0'; } int u = 0; for (int i = n - 1; i >= 0; i--) { u = u * 10 + a[i]; a[i] = u / b; u %= b; } while (n > 1 && a[n - 1] == 0) { n--; } for (int i = n - 1; i >= 0; i--) { printf("%d", a[i]); } printf("\n"); }
高精度除以单精度
https://www.luogu.com.cn/problem/P1255
楼梯有 阶,上楼可以一步上一阶,也可以一步上二阶。
编一个程序,计算共有多少种不同的走法。
一个数字,楼梯数。
输出走的方式总数。
4
5
#include <bits/stdc++.h> using namespace std; int n; int a[3020], b[3020]; void swap() { // jiaohuan a he b for (int i = 0; i < 3010; i++) { swap(a[i], b[i]); } } void add() { // a += b for (int i = 0; i < 3010; i++) { a[i] += b[i]; } for (int i = 0; i < 3010; i++) { a[i + 1] += a[i] / 10; a[i] %= 10; } } int main() { a[0] = 1; b[0] = 1; scanf("%d", &n); if (n == 0) { printf("0\n"); return 0; } for (int i = 0; i < n; i++) { // a, b -> b, a + b add(); swap(); } int l = 3010; while (a[l] == 0) { l--; } for (int i = l; i >= 0; i--) { printf("%d", a[i]); } printf("\n"); return 0; }
高精度加法算Fibonacci数,的情况非常智障,答案是而不是。
https://www.luogu.com.cn/problem/P1080
恰逢 国国庆,国王邀请 位大臣来玩一个有奖游戏。首先,他让每个大臣在左、右手上面分别写下一个整数,国王自己也在左、右手上各写一个整数。然后,让这 位大臣排成一排,国王站在队伍的最前面。排好队后,所有的大臣都会获得国王奖赏的若干金币,每位大臣获得的金币数分别是:排在该大臣前面的所有人的左手上的数的乘积除以他自己右手上的数,然后向下取整得到的结果。
国王不希望某一个大臣获得特别多的奖赏,所以他想请你帮他重新安排一下队伍的顺序,使得获得奖赏最多的大臣,所获奖赏尽可能的少。注意,国王的位置始终在队伍的最前面。
第一行包含一个整数,表示大臣的人数。
第二行包含两个整数 和 ,之间用一个空格隔开,分别表示国王左手和右手上的整数。
接下来 行,每行包含两个整数 和 ,之间用一个空格隔开,分别表示每个大臣左手和右手上的整数。
一个整数,表示重新排列后的队伍中获奖赏最多的大臣所获得的金币数。
3
1 1
2 3
7 4
4 6
2
【输入输出样例说明】
按、、 这样排列队伍,获得奖赏最多的大臣所获得金币数为 ;
按 、、 这样排列队伍,获得奖赏最多的大臣所获得金币数为;
按 、、 这样排列队伍,获得奖赏最多的大臣所获得金币数为 ;
按、、这样排列队伍,获得奖赏最多的大臣所获得金币数为;
按 、、这样排列队伍,获得奖赏最多的大臣所获得金币数为 ;
按、、 这样排列队伍,获得奖赏最多的大臣所获得金币数为 。
因此,奖赏最多的大臣最少获得 个金币,答案输出 。
【数据范围】
对于 20%的数据,有 ;
对于 40%的数据,有;
对于 60%的数据,有 ;
对于 60%的数据,保证答案不超过 ;
对于 100%的数据,有 。
NOIP 2012 提高组 第一天 第二题
国王游戏。按乘积排序然后计算答案。
需要高精度或 Python
https://www.luogu.com.cn/problem/P1727
《爱与愁的故事第二弹·compute》第一章。
中秋至,博饼声铿锵不断。爱与愁大神兴致勃勃地到学校博饼,结果抱回家的只有一秀二举。爱与愁大神十分生气,打电话给月落乌啼:“喂,帮我算出圆周率小数点后n(n<=10000)位,速度……”然后就挂了电话,也不知道月落乌啼正准备去上课。月落乌啼只好请到了你,让你编一个程序求出圆周率小数点后n位。
只有一行:n
若干行。
第一行:3.
第二行开始:圆周率的小数部分。10个数一个空格,50个数一次回车。
100
3.
1415926535 8979323846 2643383279 5028841971 6939937510
5820974944 5923078164 0628620899 8628034825 3421170679
对于 的数据,。
对于 的数据,。
时限: 点 秒, 点 秒, 点 秒, 点 秒。
算Pi,用
https://en.wikipedia.org/wiki/Machin-like_formula
的公式
https://www.luogu.com.cn/problem/P1729
《爱与愁的故事第二弹·compute》最终章。
自然对数的底数e是一个著名的无理数,其近似值为2.718281828…有计算e的公式如下:
![](https://cdn.luogu.com.cn/upload/pic/520.png)
其中n!表示n的阶乘,即n!=1*2*3*…*n。
月落乌啼竟然这么快就回复了圆周率小数点后10000位?!不可能,他肯定求了别人。爱与愁大神再次为难月落乌啼:“帮我算一算e后n(n<=10000)位,速度!!!”月落乌啼想求别人,结果他发现由于刚才跟你通话已经用完了手机的所有电。关键时刻只能靠自己。如果现在你是他,你会怎么编这个程序?
只有一行:n
若干行。
第一行:2.
第二行开始:e的小数部分。10个数一个空格,50个数一次回车。
100
2.
7182818284 5904523536 0287471352 6624977572 4709369995
9574966967 6277240766 3035354759 4571382178 5251664274
30%数据:n<=1000
100%数据:n<=10000
时限:全部1秒
算E,用定义就可以了。
https://www.luogu.com.cn/problem/P1096
给定、、三根足够长的细柱,在柱上放有个中间有孔的圆盘,共有个不同的尺寸,每个尺寸都有两个相同的圆盘,注意这两个圆盘是不加区分的(下图为的情形)。
现要将这些圆盘移到柱上,在移动过程中可放在柱上暂存。要求:
(1)每次只能移动一个圆盘;
(2)、、三根细柱上的圆盘都要保持上小下大的顺序;
任务:设为个圆盘完成上述任务所需的最少移动次数,对于输入的,输出。
一个正整数,表示在柱上放有个圆盘。
一个正整数, 为完成上述任务所需的最少移动次数。
1
2
2
6
【限制】
对于的数据,
对于的数据,
【提示】
设法建立与的递推关系式。
#include <bits/stdc++.h> using namespace std; stringstream ss; string s; int n; int main() { cin >> n; ss << fixed << setprecision(0) << pow(2, n + 1); ss >> s; // s = ss.str(); s[s.size() - 1] -= 2; cout << s << endl; return 0; }
输入,输出,可以利用精确输出double的特性。
https://www.luogu.com.cn/problem/P1760
直达通天路·小A历险记第四篇
在你的帮助下,小A成功收集到了宝贵的数据,他终于来到了传说中连接通天路的通天山。但是这距离通天路仍然有一段距离,但是小A突然发现他没有地图!!!但是幸运的是,他在山脚下发现了一个宝箱。根据经验判断(小A有经验吗?),地图应该就在其中!在宝箱上,有三根柱子以及在一根柱子上的n个圆盘。小A在经过很长时间判断后,觉得这就是hanoi塔!(这都要琢磨)。但是移动是需要时间的,所以小A必须要通过制造延寿药水来完成这项任务。现在,他请你告诉他需要多少步完成,以便他造足够的延寿药水.。时限1s。
一个数n,表示有n个圆盘
一个数s,表示需要s步。
31
2147483647
15
32767
对于所有数据n<=15000
很容易的练手题哦!
#include <bits/stdc++.h> using namespace std; int a[5020]; int b[5020]; int main() { int n; scanf("%d", &n); a[0] = 1; for (int i = 1; i <= n; i++) { for (int j = 0; j < 5010; j++) { a[j] *= 2; } for (int j = 0; j < 5010; j++) { a[j + 1] += a[j] / 1000000000; a[j] %= 1000000000; } } int l = 5010; while (a[l] == 0) { l--; } a[0]--; printf("%d", a[l]); for (int j = l - 1; j >= 0; j--) { printf("%09d", a[j]); } printf("\n"); return 0; }
输入,输出,可以利用精确输出double的特性。
Error: ENOENT: no such file or directory, open '/Users/wwwwodddd/Dropbox/Github/Informatics/problems/CF1700B.md'
https://codeforces.com/problemset/problem/1144/E
https://www.luogu.com.cn/problem/CF1144E
https://atcoder.jp/contests/abc174/tasks/abc174_c
输入K,问最小连续几个7组成的数字,是K的倍数?无解输出-1
#include <bits/stdc++.h> using namespace std; int k, a; int main() { cin >> k; for (int i = 1; i <= k; i++) { a = (10 * a + 7) % k; if (a == 0) { cout << i << endl; return 0; } } cout << -1 << endl; return 0; }
Σ[k=0..10^100]floor(X/10^k)
https://atcoder.jp/contests/abc233/tasks/abc233_e
输入一个数字X,问所有前缀和是多少?
比如输入 1225 应该输出 1225 + 122 + 12 + 1 = 1360
#include <bits/stdc++.h> using namespace std; string s; int n, a[500020]; int main() { cin >> s; reverse(s.begin(), s.end()); n = s.size(); for (int i = 0; i < n; i++) { a[i] = s[i] - '0'; } for (int i = n - 1; i >= 0; i--) { a[i] += a[i + 1]; } n += 1; for (int i = 0; i < n; i++) { a[i + 1] += a[i] / 10; a[i] %= 10; } while (a[n - 1] == 0) { n--; } for (int i = n - 1; i >= 0; i--) { cout << a[i]; } cout << endl; return 0; }