前言
什么是高精度?
高精度是一種算法,用於計算各種大數的運算。
什么意思呢?
我舉個栗子。首先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!