zoukankan      html  css  js  c++  java
  • 高精度学习笔记 & 纯高精度题的洛谷题解

    前言

    什么是高精度?
    高精度是一种算法,用于计算各种大数的运算。
    什么意思呢?
    我举个栗子。首先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!

  • 相关阅读:
    inner join on, left join on, right join on讲解(转载)
    ref 与 out
    Shell基础01
    Python 基础01
    linux基础03
    Shell基础02
    linux基础02
    Linux基础01
    linux基础05
    linux基础04
  • 原文地址:https://www.cnblogs.com/crab-in-the-northeast/p/high-precision.html
Copyright © 2011-2022 走看看