前言
什么是高精度?
高精度是一种算法,用于计算各种大数的运算。
什么意思呢?
我举个栗子。首先OI界有一句金玉良言:
即使题面出的水,只要数据够变态,难度指数级上升。
对于著名的水题和各种神仙的装弱工具A+B Problem
,它的数据范围是什么呢?
(|a|,|b| le 10^9)
嗯,所以我们可以放心大胆的这么写:
#include <iostream>
#include <cstdio>
int main() {
int a,b;
std :: cin >> a >> b;
std :: cout << a + b << std :: endl;
return 0;
}
但是,如果我把数据范围改成这样:
(|a|,|b| le 10^{18})
你就不能用int
了,而得用long long
:
#include <iostream>
#include <cstdio>
int main() {
long long a,b;
std :: cin >> a >> b;
std :: cout << a + b << std :: endl;
return 0;
}
最后,我要是把数据范围改成这样:
(0 le a,b le 10^{500})
这就是luogu p1601的原题。
你根本就找不到一种数据类型能存这样大的数。
那怎么办呢?自己造一种数据类型?
没错,就是这样。这种算法,就叫做高精度。
面对这样大的数,蟹蟹唯一想到的办法,就是用数组存储每一个位数。
但这样,肯定不能直接加减乘除运算,我们得自定义。
怎么自定义呢?
想想我们小时候求加减乘除的方法。没错,列竖式。
同样的,这里我们也用两个需要进行运算的数进行竖式模拟运算。这种方法虽然笨,但真的没有什么别的方法了。
怎么来写高精度呢?具体的竖式应该如何去操作呢?
请接着看。
说明一下,现在noip很少考到高精度了,但是我们为了保险,还是学习一下高精度。学总比不学强嘛。
再说明:本教程中的高精度只涉及到非负整数。负数和浮点数本教程中不考虑。
正式开始(最初版本)
准备工作
首先我们需要准备好程序构架。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cassert>
const int maxn = 4005;//运算时最多的位数,也就是说这个值取决于数据范围中运算过程中数值可能的最大位数。
int main() {
}
然后定义一个结构体。这是一种高阶玩法,到头来我们可以直接用ubigint定义数,来进行高精度的运算,到时候直接加加减减就ok。
struct ubigint() {
}
赋值运算符
首先我们来看一些基本的赋值:
int s[maxn], len;
//一些基础的东西
ubigint() { memset(s, 0, sizeof(s)); }
ubigint(int num) { *this = num; }
ubigint(char *num) { *this = num; }
ubigint operator = (int num) {
char c[maxn];
sprintf(c, "%d", num);
*this = c;
return *this;
}
ubigint operator = (const char *num) {
len = strlen(num);
for(int i = 0; i < len; i++)
s[i] = num[len - i - 1] - '0';//请注意这里是倒过来赋值的,也就是说得到的数组其实直接输出,数是反着的。为什么呢?一般我们列竖式都是从后往前的,所以我们把数组进行倒序处理,这样我们在程序中就可以正序遍历了。
return *this;
}
这些都是最基本的定义赋值之类的东西,我不再赘述。
关系运算符
接下来我们来重载一些关系运算符:
//关系运算符
bool operator < (const ubigint &b) const {
if(len != b.len) return len < b.len;//比位数
for(int i = len - 1; i >= 0; i--)//诸位比较
if(s[i] != b.s[i])
return s[i] < b.s[i];
return false;
}
bool operator > (const ubigint &b) const {return b < *this; }
bool operator <= (const ubigint &b) const {return !(b < *this);}
bool operator >= (const ubigint &b) const {return !(*this < b);}
bool operator == (const ubigint &b) const {return !(b < *this) && !(b > *this);}
bool operator != (const ubigint &b) const {return b < *this || *this < b;}
我解释一下小于号。二年级数学中就有两个数比较大小的方法,先比位数,后从高位到低位诸位比较。
位数就是两个数组的长度,如果len < b.len证明小于号判断成立,如果len > b.len说明小于号判断不成立。
如果位数相等,从高位到低位诸位比较,如果当前数字不一样,则对当前的数字的大小关系来判断这两个数的大小关系。
如果诸位比较完成,说明两个数相等。不过这个时候是不满足小于的,所以返回false。
然后其他的就是各种套用小于号,因为我懒得在写一遍了qaq(不要打蟹蟹呀嘤嘤嘤)
算术运算符
加法
首先先来康加法。
首先小学我们是咋算加法的呢?对了,列竖式。
我们可以从最后一位(再次提醒,请注意本教程中数组存储是倒着的,所以程序中是从第一个字符开始遍历)开始无脑加法,然后把得到的结果放在结果的对应位置。
吗?
你在想什么啊,进位我们还得考虑。
于是,我们就可以相加,如果没进位直接放,如果进位了把得到的结果%10放到结果的那个位置,然后那个位置的前面那个位置加上1(表示进位)。
但是我们显然还漏掉了一种情况,当两个加数的位数不相同。其实这个问题根本不算什么问题,只需要特判好了。
这样就不难得到代码了:
ubigint operator + (const ubigint &b) const {
ubigint res;
res.len = 0;
for(int i = 0, x = 0; x || i < len || i < b.len; i++, x /= 10) {
if(i < len) x += s[i];
if(i < b.len) x += b.s[i];
res.s[res.len++] = x % 10;
}
return res;
}
减法
然后是减法。
减法其实和加法差不多,也是按位相减,如果不够,借1.
顺便说明一下,这里的减法不支持被减数小于减数的情况,也就是结果必须大于0.
ubigint operator - (const ubigint &b) const {
assert(*this >= b);
ubigint res;
res.len = 0;
int x;
for(int i = 0, g = 0; i < len; i++) {
x = s[i] - g;
if(i < b.len) x -= b.s[i];
if(x >= 0) g = 0;
else {
x += 10;
g = 1;
}
res.s[res.len++] = x;
}
res.clear_zero();//注意到这里了没有?
return res;
}
如果你是一个细心的人,会发现上面多了一个clear_zero()
。
这是什么?
一般在高精度运算中,可能会出现结果是正确的,但是结果之前有好几个0.这种0叫做前导0,高精度运算中常常不能避免,所以我们需要定义一个clear_zero()
函数去掉这些多余的前导0.
怎么清呢?我们只需要定义这样的代码:
void clear_zero() {
while(len > 1 && !s[len - 1]) len--;
}
你可能会问了,为什么是len > 1,而不是len > 0呢?
前导0不是遇到不是0的情况才停的,你想啊,如果这个数是0,那这个0可不算是前导0.所以我们不清个位,个位都有前导0只能说明这个数是0,需要特殊对待。
乘法
乘法这点就比较难了,蟹蟹陪你耐心分析。(其实也不是很难辣,放松心态呀qaq)
小学我们是怎么学的?每一位按位相乘,相乘的结果再进行相加。
但存储相乘的结果又是一笔开销,我们不如思维跳跃一下,把每一位相乘的结果直接对应到res数组里,岂不美滋滋?
这里我们有一个定律,第一个数从后往前第i位数 和 第二个数从后往前第j位数 相乘 的结果 最后 会相加到 结果的 从后往前第i+j位上。
如果你不懂,不如先列几个竖式,感受感受下?这里一定要弄懂哦。
但是进位也是个麻烦事。每一位的数在枚举i,j的时候随时都会变,随时进位总觉得不好。我们还是都累积完了,再一一进位吧,这样比较好。(个人习惯辣)
于是就有了如下的代码:
ubigint operator * (const ubigint &b) const {
ubigint res;
res.len = len + b.len;
for(int i = 0; i < len; i++)
for(int j = 0; j < b.len; j++)
res.s[i + j] += s[i] * b.s[j];
for(int i = 0; i < res.len - 1; i++) {
res.s[i + 1] += res.s[i] / 10;
res.s[i] %= 10;
}
res.clear_zero();
return res;
}
除法
其实除法就是乘法的逆运算,和乘法也差不多,就像加法和减法一样。不过除法在试除这个方面是有技巧的,我们可以二分试除。
ubigint operator / (const ubigint &b) const {
assert(b > 0);
ubigint res = *this,m;
for(int i = len - 1; i >= 0; --i) {
m = m * 10 + s[i];
res.s[i] = midsearch(b, m);
m = m - b * res.s[i];
}
res.clear_zero();
return res;
}
midsearch函数:
int midsearch(const ubigint &b, const ubigint &m) const {
int L = 0, R = 9, mid;
while(1) {
mid = (L + R) >> 1;
if(b * mid <= m) {
if(b * (mid + 1) > m) return mid;
else L = mid;
}
else R = mid;
}
}
模法
除法都完了,但是别忘了,我们还有魔法。我不会告诉你这其实是%法的,%%% tql orz
取模运算其实特别简单,把上述代码中,最后return m;
就可以了。
ubigint operator % (const ubigint &b) const {
assert(b > 0);
ubigint res = *this;
ubigint m;
for(int i = len - 1; i >= 0; i--) {
m = m * 10 + s[i];
res.s[i] = midsearch(b,m);
m = m - b * res.s[i];
}
m.clear_zero();
return m;
}
扩展赋值运算符
我们还可以通过以上的五则运算,扩展出+= -= *= /= %= 。 这就很简单辣。
ubigint& operator += (const ubigint &b) {*this = *this + b; return *this;}
ubigint& operator -= (const ubigint &b) {*this = *this - b; return *this;}
ubigint& operator *= (const ubigint &b) {*this = *this * b; return *this;}
ubigint& operator /= (const ubigint &b) {*this = *this / b; return *this;}
ubigint& operator %= (const ubigint &b) {*this = *this % b; return *this;}
输入输出流
最后的最后,我们搞定输入输出流吧。
std :: istream& operator >>(std :: istream &in, ubigint &x) {
std :: string s;
in >> s;
x = s.c_str();
return in;
}
std :: ostream& operator <<(std :: ostream &out,ubigint x) {
out << x.to_str();
return out;
}
哦对了差点忘记说,我们为了输出ubigint
这个类型,还需要定义一个to_str
函数,把ubigint
类型转成std :: string
型。很简单辣。
std :: string to_str() {
std :: string res = "";
for(int i = 0; i < len; i++) res = (char)(s[i] + '0') + res;
return res;
}
至此我们的ubigint
就定义完了,拍张合照!
总体代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cassert>
const int maxn = 30005;
struct ubigint {
int s[maxn], len;
//一些基础的东西
ubigint() { memset(s, 0, sizeof(s)); }
ubigint(int num) { *this = num; }
ubigint(char *num) { *this = num; }
ubigint operator = (int num) {
char c[maxn];
sprintf(c, "%d", num);
*this = c;
return *this;
}
ubigint operator = (const char *num) {
len = strlen(num);
for(int i = 0; i < len; i++)
s[i] = num[len - i - 1] - '0';
return *this;
}
int midsearch(const ubigint &b, const ubigint &m) const {
int L = 0, R = 9, mid;
while(1) {
mid = (L + R) >> 1;
if(b * mid <= m) {
if(b * (mid + 1) > m) return mid;
else L = mid;
}
else R = mid;
}
}
std :: string to_str() {
std :: string res = "";
for(int i = 0; i < len; i++) res = (char)(s[i] + '0') + res;
return res;
}
void clear_zero() {
while(len > 1 && !s[len - 1]) len--;
}
//关系运算符
bool operator < (const ubigint &b) const {
if(len != b.len) return len < b.len;
for(int i = len - 1; i >= 0; i--)
if(s[i] != b.s[i])
return s[i] < b.s[i];
return false;
}
bool operator > (const ubigint &b) const {return b < *this; }
bool operator <= (const ubigint &b) const {return !(b < *this);}
bool operator >= (const ubigint &b) const {return !(*this < b);}
bool operator == (const ubigint &b) const {return !(b < *this) && !(b > *this);}
bool operator != (const ubigint &b) const {return b < *this || *this < b;}
ubigint operator + (const ubigint &b) const {
ubigint res;
res.len = 0;
for(int i = 0, x = 0; x || i < len || i < b.len; i++, x /= 10) {
if(i < len) x += s[i];
if(i < b.len) x += b.s[i];
res.s[res.len++] = x % 10;
}
return res;
}
ubigint operator - (const ubigint &b) const {
assert(*this >= b);
ubigint res;
res.len = 0;
int x;
for(int i = 0, g = 0; i < len; i++) {
x = s[i] - g;
if(i < b.len) x -= b.s[i];
if(x >= 0) g = 0;
else {
x += 10;
g = 1;
}
res.s[res.len++] = x;
}
res.clear_zero();
return res;
}
ubigint operator * (const ubigint &b) const {
ubigint res;
res.len = len + b.len;
for(int i = 0; i < len; i++)
for(int j = 0; j < b.len; j++)
res.s[i + j] += s[i] * b.s[j];
for(int i = 0; i < res.len - 1; i++) {
res.s[i + 1] += res.s[i] / 10;
res.s[i] %= 10;
}
res.clear_zero();
return res;
}
ubigint operator / (const ubigint &b) const {
assert(b > 0);
ubigint res = *this,m;
for(int i = len - 1; i >= 0; i--) {
m = m * 10 + s[i];
res.s[i] = midsearch(b, m);
m = m - b * res.s[i];
}
res.clear_zero();
return res;
}
ubigint operator % (const ubigint &b) const {
assert(b > 0);
ubigint res = *this;
ubigint m;
for(int i = len - 1; i >= 0; i--) {
m = m * 10 + s[i];
res.s[i] = midsearch(b,m);
m = m - b * res.s[i];
}
m.clear_zero();
return m;
}
ubigint& operator += (const ubigint &b) {*this = *this + b; return *this;}
ubigint& operator -= (const ubigint &b) {*this = *this - b; return *this;}
ubigint& operator *= (const ubigint &b) {*this = *this * b; return *this;}
ubigint& operator /= (const ubigint &b) {*this = *this / b; return *this;}
ubigint& operator %= (const ubigint &b) {*this = *this % b; return *this;}
};
std :: istream& operator >>(std :: istream &in, ubigint &x) {
std :: string s;
in >> s;
x = s.c_str();
return in;
}
std :: ostream& operator <<(std :: ostream &out,ubigint x) {
out << x.to_str();
return out;
}
int main() {
}
压位优化
其实这样的高精度我们还能继续优化。来看下面这个例子:
显然我们在ubigint
类中,是一位一位加的。但是我们为什么不能这样??
之前算四遍,现在算两遍。我们可以想想,能不能直接算一遍呢?
行吧,这就是直接算了。
但是我们为什么不能这样直接算一遍呢?这其实就是高精度存在的意义,根本不存在一种数据方式来算这么大的加法。比如以下就是一个例子:
这可不能直接算。但是我们有刚刚的方法,可以尽量的分割这两个数:
这样算的话,就变成了两位两位的算,算的次数减少了,就会有优化。
我们还可以压4位:
算的方法就更少啦。
我们不妨思考一个问题,我们这样分数,分得的每一个区域允许的最大位数是多少?
如果是int
型,显然是9
位。因为int
的范围最大是2147483647,此时9位相加相减,不会爆炸。所以我们最好这样:
这种方法,就叫做压位。使用压位的高精,就叫做压位高精。
平常我们所说的压x位,其实就是把数分成若干部分,使得每一个部分最大是x位数。(为什么是最大呢,因为有可能没分完。)
有好奇心强的小伙伴可能会问了,压位看起来算的次数减少,可每一个位置内置的加法算法不是也增多了吗?这样岂不是拆东墙补西墙?
我谔谔,C++内置的加法算法怎么说也是经过层层优化的,增加位数并不会浪费多长的时间,至少不像我们写的高精度,要不然C++也走不到这样高的巅峰。我们有更高的靠山,就尽量去靠。
代码:
/*
* @Author: crab-in-the-northeast
* @Date: 2020-03-19 23:07:41
* @Last Modified by: crab-in-the-northeast
* @Last Modified time: 2020-03-19 01:07:24
*/
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cassert>
#include <cstring>
#include <string>
int max(int a,int b) {
return a > b ? a : b;
}
static const int base = 1000000000;
static const int width = 9;
struct ubigint {
typedef long long ll;
ll s[8005] = {0};
int len = 1;
ubigint(){}
ubigint(const ubigint& x) {
for(int i = 0; i < x.len; i++)
this -> s[i] = x.s[i];
this -> len = x.len;
}
ubigint(const int& x) {
int tmp = x;
this -> len = 0;
do{
this -> s[this -> len++] = tmp % base;
tmp /= base;
}while(tmp);
}
ubigint operator = (const std :: string& str) {
std :: string tmp = str;
while(tmp.length() > width) {
this -> s[this -> len - 1] = atoi(tmp.substr(tmp.length() - width).c_str());
tmp.erase(tmp.length() - width);
this -> len++;
}
this -> s[this -> len - 1] = atoi(tmp.c_str());
return *this;
}
void clear_zero() {
while(this -> len > 1 && !(this -> s[this -> len - 1])) this -> len--;
}
ubigint operator = (const ubigint& x) {
this -> len = x.len;
for(register int i = 0; i < this -> len; i++)
this -> s[i] = x.s[i];
return *this;
}
bool operator < (const ubigint& b) const {
if(this -> len != b.len) return this -> len < b.len;
for(register int i = this -> len - 1; i >= 0; i--)
if(this -> s[i] != b.s[i])
return this -> s[i] < b.s[i];
return false;
}
bool operator > (const ubigint& b) const {
return b < *this;
}
bool operator <= (const ubigint& b) const {
return !(b < *this);
}
bool operator >= (const ubigint& b) const {
return !(*this < b);
}
bool operator != (const ubigint& b) const {
return *this < b || b < *this;
}
bool operator == (const ubigint& b) const {
return !(b < *this) && !(*this < b);
}
ubigint operator + (const ubigint& b) const {
ubigint res;
res.len = max(this -> len, b.len);
for(register int i = 0, last = 0; i < res.len; i++) {
res.s[i] = this -> s[i] + b.s[i] + last;
last = res.s[i] / base;
res.s[i] %= base;
if(i == res.len - 1 && last) res.len++;
}
res.clear_zero();
return res;
}
ubigint operator - (const ubigint& b) const {
assert(*this >= b);
ubigint res = *this;
for(register int i = 0, last = 0; i < this -> len; i++) {
res.s[i] -= b.s[i] + last;
if(res.s[i] < 0) {
res.s[i] += base;
last = 1;
}else last = 0;
}
res.clear_zero();
return res;
}
ubigint operator * (const ubigint& b) const {
if(*this == 0 || b == 0) return ubigint(0);
//if(*this == 1) return b;
//if(b == 1) return *this;
ubigint res;
res.len = this -> len + b.len + 1;
for(register int i = 0; i < this -> len; i++) {
for(register int j = 0; j < b.len; j++) {
res.s[i + j] += this -> s[i] * b.s[j];
res.s[i + j + 1] += res.s[i + j] / base;
res.s[i + j] %= base;
}
}
res.clear_zero();
return res;
}
ubigint operator / (const ubigint& b) const {
if(b > *this) return 0;
//if(b == *this) return 1;
ubigint res = 0, div = b, mod = *this;
while(div * ubigint(base) <= *this) div *= ubigint(base);
for(;;) {
int l = 1, r = base, mid;
if(mod >= div) {
while(r > l + 1) {
int mid = l + r >> 1;
if(div * ubigint(mid) > mod) r = mid;
else l = mid;
}
mod -= div * ubigint(l);
res.s[res.len - 1] += l;
}
if(div == b) break;
res.len++;
for(register int i = 1; i < div.len; i++)
div.s[i - 1] = div.s[i];
div.s[div.len - 1] = 0;
div.len--;
}
std :: reverse(res.s, res.s + res.len);
res.clear_zero();
return res;
}
ubigint operator % (const ubigint& b) const {
return *this - (*this / b * b);
}
ubigint operator += (const ubigint& b) {*this = *this + b; return *this;}
ubigint operator -= (const ubigint& b) {*this = *this - b; return *this;}
ubigint operator *= (const ubigint& b) {*this = *this * b; return *this;}
ubigint operator /= (const ubigint& b) {*this = *this / b; return *this;}
ubigint operator %= (const ubigint& b) {*this = *this % b; return *this;}
friend std :: istream& operator >> (std :: istream& in, ubigint& b) {
std :: string str;
in >> str;
b = str;
return in;
}
friend std :: ostream& operator << (std :: ostream& out, ubigint b) {
out << b.s[b.len - 1];
for(register int i = b.len - 2; i >= 0; i--) {
int div = base / 10;
while(b.s[i] < div) {
std :: cout << 0;
div /= 10;
}
if(b.s[i]) out << b.s[i];
}
return out;
}
};
int main() {
ubigint a,b;
std :: cin >> a >> b;
std :: cout << a + b << std :: endl;
if(a >= b) std :: cout << a - b << std :: endl;
else std :: cout << '-' << b - a << std :: endl;
std :: cout << a * b << std :: endl;
std :: cout << a / b << std :: endl;
std :: cout << a % b << std :: endl;
return 0;
}
这是静态数组版本的,我们还可以用动态数组vector
来重构代码。不过这样时间需要的就长啦(写这个的时候懒得写this ->
了qvq)
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <cassert>
#include <vector>
int max(int a,int b) {
return a > b ? a : b;
}
struct ubigint {
typedef unsigned long long ull;
static const int base = 100000000;
static const int width = 8;
std :: vector<int> s;
ubigint& clear_zero() {
while(!s.back() && s.size() > 1) s.pop_back();
return *this;
}
ubigint(ull num = 0) {*this = num;}
ubigint(std :: string str) {*this = str;}
ubigint& operator = (ull num) {
s.clear();
do {
s.push_back(num % base);
num /= base;
}while(num);
return *this;
}
ubigint& operator = (const std :: string& str) {
s.clear();
int x;
int len = (str.length() - 1) / width + 1;
for(int i = 0; i < len; i++) {
int endidx = str.length() - i * width;
int startidx = max(0, endidx - width);
int x;
sscanf(str.substr(startidx, endidx - startidx).c_str(), "%d", &x);
s.push_back(x);
}
return (*this).clear_zero();
}
bool operator < (const ubigint& b) const {
if(s.size() != b.s.size()) return s.size() < b.s.size();
for(int i = s.size() - 1; i >= 0; i--)
if(s[i] != b.s[i])
return s[i] < b.s[i];
return false;
}
bool operator > (const ubigint& b) const {return b < *this; }
bool operator <= (const ubigint& b) const {return !(b < *this);}
bool operator >= (const ubigint& b) const {return !(*this < b);}
bool operator == (const ubigint& b) const {return !(b < *this) && !(b > *this);}
bool operator != (const ubigint& b) const {return b < *this || *this < b;}
ubigint operator + (const ubigint& b) const {
ubigint res;
res.s.clear();
for(int i = 0, x = 0; x || i < s.size() || i < b.s.size(); i++, x /= base) {
if(i < s.size()) x += s[i];
if(i < b.s.size()) x += b.s[i];
res.s.push_back(x % base);
}
return res.clear_zero();
}
ubigint operator - (const ubigint& b) const {
assert(*this >= b);
ubigint res;
res.s.clear();
for(int i = 0, last = 0;last || i < s.size() || i < b.s.size();i++) {
int x = s[i] + last;
if(i < b.s.size()) x -= b.s[i];
if(x < 0) {
last = -1;
x += base;
}else last = 0;
res.s.push_back(x);
}
return res.clear_zero();
}
ubigint operator * (const ubigint& b) const {
std :: vector<ull> tmp(s.size() + b.s.size(),0);
ubigint res;
res.s.clear();
for(int i = 0; i < s.size(); i++)
for(int j = 0; j < b.s.size(); j++)
tmp[i + j] += ull(s[i]) * b.s[j];
ull last = 0;
for(int i = 0; last || i < tmp.size(); i++) {
ull x = tmp[i] + last;
res.s.push_back(x % base);
last = x / base;
}
return res.clear_zero();
}
int midsearch(const ubigint& b, const ubigint& m) const {
int l = 0, r = base - 1;
while(1) {
int mid = l + r >> 1;
if(b * mid <= m && b * (mid + 1) > m) return mid;
if(b * mid <= m) l = mid;
else r = mid;
}
}
ubigint operator / (const ubigint& b) const {
assert(b > 0);
ubigint res = *this, mod;
for(int i = s.size() - 1; i >= 0; i--) {
mod = mod * base + s[i];
res.s[i] = midsearch(b, mod);
mod -= b * res.s[i];
}
return res.clear_zero();
}
ubigint operator % (const ubigint& b) const {
assert(b > 0);
ubigint res = *this, mod;
for(int i = s.size() - 1; i >= 0; i--) {
mod = mod * base + s[i];
res.s[i] = midsearch(b, mod);
mod -= b * res.s[i];
}
return mod.clear_zero();
}
ubigint& operator += (const ubigint& b) {*this = *this + b; return *this;}
ubigint& operator -= (const ubigint& b) {*this = *this - b; return *this;}
ubigint& operator *= (const ubigint& b) {*this = *this * b; return *this;}
ubigint& operator /= (const ubigint& b) {*this = *this / b; return *this;}
ubigint& operator %= (const ubigint& b) {*this = *this % b; return *this;}
friend std :: istream& operator >> (std :: istream& in, ubigint& x) {
std :: string str;
if(!(in >> str)) return in;
x = str;
return in;
}
friend std :: ostream& operator << (std :: ostream& out, ubigint x) {
out << x.s.back();
for(int i = x.s.size() - 2; i >= 0; i--) {
char buf[20];
sprintf(buf, "%08d", x.s[i]);
for(int j = 0; j < strlen(buf); j++)
out << buf[j];
}
return out;
}
};
ubigint a,b;
int main() {
std :: cin >> a >> b;
std :: cout << a + b << std :: endl;
if(a >= b) std :: cout << a - b << std :: endl;
else std :: cout << "-" << b - a << std :: endl;
std :: cout << a * b << std :: endl;
std :: cout << a / b << std :: endl;
std :: cout << a % b << std :: endl;
return 0;
}
当然这样的高精度还存在一些不足,比如不支持负数,乘法没有FFT
优化等。但是考虑到竞赛中不会出现这么duliu
的高精度,本文不作处理。
小试牛刀
最后我们来小试几道牛刀吧。
luogu p1601(高精A + B):
int main() {
ubigint a, b;
std :: cin >> a >> b;
std :: cout << a + b << std :: endl;
return 0;
}
luogu p2142(高精A - B):
这道题需要负数诶!不过没关系,我们有一个小伎俩:
int main() {
ubigint a,b;
std :: cin >> a >> b;
if(a < b) std :: cout << "-" << b - a << std :: endl;
else std :: cout << a - b<< std :: endl;
return 0;
}
是不是既简单又方便的偷懒方式呢~
luogu p1303(高精A * B):
int main() {
ubigint a, b;
std :: cin >> a >> b;
std :: cout << a * b << std :: endl;
return 0;
}
luogu p1480 & luogu p2005(高精A / B):
后面的那个是前面的那个的升级版。双倍经验美滋滋
int main() {
ubigint a,b;
std :: cin >> a >> b;
std :: cout << a / b << std :: endl;
return 0;
}
luogu p1932(高精合集):
int main() {
ubigint a,b;
std :: cin >> a >> b;
std :: cout << a + b << std :: endl;
if(a >= b) std :: cout << a - b << std :: endl;
else std :: cout << '-' << b - a << std :: endl;
std :: cout << a * b << std :: endl;
std :: cout << a / b << std :: endl;
std :: cout << a % b << std :: endl;
return 0;
}
因为原题中的减法涉及到了负数,所以减法做了这样的处理qvq(别打我啊qvq)
而且注意,亲测本题如果用动态数组版本是无法AC的,结果是80TLE
,必须得吸氧才能AC
。所以这道题请使用静态数组版本。
luogu p1009(阶乘之和):
int main() {
int n;
ubigint ans;
std :: cin >> n;
for(int i = 1; i <= n; i++) {
ubigint mul = 1;
for(int j = 2; j <= i; j++)
mul *= j;
ans += mul;
}
std :: cout << ans << std :: endl;
return 0;
}
luogu p1591(阶乘数码,一个数的阶乘中某个数码出现了几次):
直接按照题意模拟,没啥好说的吧qaq。
int main() {
int T;
std :: cin >> T;
while(T--) {
int n, num, ans = 0;
std :: cin >> n >> num;
ubigint res = 1;
for(int i = 2; i <= n; i++)
res *= i;
ull tmp = res.s.back();
while(tmp) {
if(tmp % 10 == num) ans++;
tmp /= 10;
}
for(int i = res.s.size() - 2; i >= 0; i--) {
char buf[20];
sprintf(buf, "%08d", res.s[i]);
for(int j = 0; j < strlen(buf); j++)
if(buf[j] - '0' == num) ans++;
}
std :: cout << ans << std :: endl;
}
}
luogu p1255(数楼梯):
此题的主要思路是递推,套上了高精度的外衣而已。但是这个过于简单就归于高精模板里吧。
const int maxn = 5005;
int n;
ubigint a[maxn] = {0, 1, 2};
int main() {
std :: cin >> n;
if(n <= 2) {
std :: cout << a[n] << std :: endl;
return 0;
}
for(int i = 3; i <= n; i++)
a[i] = a[i - 1] + a[i - 2];
std :: cout << a[n] << std :: endl;
return 0;
}
qvq别忘了点个赞再走呀qvq!