高精度模板【重载运算符+压位】

昨天做一道DP的题(矩阵取数游戏),本来是很简单的,但是要用高精度,又不想用__int128水过去(谁让NOIP不让),于是自己打了一个小时,最后成功挂了。。。

于是本蒟蒻痛定思痛,感觉高精度还是重载运算符好用啊,就花了几个小时打了一个自以为比较好记好用高精度模板:

注意暂不支持负数,如果发现有bug欢迎指出。

/*采用重载运算符,压B位
支持高精数赋值与输出、高精加(减)高精、高精乘低(高)精、高精除(模)低精、高精的比较 
注意:暂不支持负数!!!*/
#include<cstdio>
#include<cstring>
#include<stack>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long LL;
const int N=128,B=4;
const LL base=1e4;//压B位,base为1eB 
struct HP{
private:
    int len;LL a[N];
    inline void clear(){while(!a[len]&&len>1) --len;}//清除前导0 
public:
    HP(){ //结构体初始化 
        memset(a,0ll,sizeof a);len=0;
    }    
    //注意因为是压B位的,所以输出要特殊处理 
    void print(){//输出高精度数 
        printf("%lld",a[len]);
        for(int i=len-1;i>0;--i)
            for(int j=base/10;j>0;j/=10) printf("%lld",a[i]/j%10);
        puts("");
    }
    /*以下是重载运算符*/ 
    HP& operator =(LL x){//用LL初始化高精度数    HP后加了&才可以实现像a=b=c这样连续赋值 
        stack<char> st;string s;
        if(!x) return *this="0";
        while(x){
            st.push(x%10+'0');
            x/=10;
        }
        while(!st.empty()) s+=st.top(),st.pop();//其实就是把int转换为string再调用init_s()
        return *this=s;
    }
    HP& operator =(const string &s){//用string初始化高精度数
        memset(a,0ll,sizeof a);len=0;//这里赋值为0才可以保证重复赋值时不会出锅。。。 
        int l=s.length();
        for(int i=0;i<=l;++i){
            int j=(l-i+(B-1))/B;//因为压B位所以要特殊处理 
            a[j]=(a[j]<<1)+(a[j]<<3)+(s[i]^48);
        }
        len=(l+(B-1))/B;
        return *this;
    } 
    HP operator +(const HP &x)const{//高精加高精 
        HP res;res.len=max(len,x.len);LL k=0ll;
        for(int i=1;i<=res.len;++i){
            res.a[i]=a[i]+x.a[i]+k;
            k=res.a[i]/base;
            res.a[i]%=base;
        }
        if(k>0) res.a[++res.len]=k;
        return res;
    }
    HP operator -(const HP &x)const{//高精减高精(注意这里默认被减数大于减数,否则会出错) 
        HP res=*this;//相当于用res拷贝被减数 
        for(int i=1;i<=len;++i){
            res.a[i]-=x.a[i];
            if(res.a[i]<0) --res.a[i+1],res.a[i]+=base;
        }
        res.clear();
        return res;
    }
    HP operator *(const LL &x)const{//高精乘低精 
        HP res=*this;
        for(int i=1;i<=len;++i) res.a[i]*=x;
        for(int i=1;i<=len;++i) res.a[i+1]+=res.a[i]/base,res.a[i]%=base;
        int &end=res.len;
        while(res.a[end+1]>0){++end;res.a[end+1]+=res.a[end]/base,res.a[end]%=base;}
        return res; 
    }
    HP operator *(const HP &x)const{//高精乘高精 
        HP res;res.len=len+x.len;
        for(int i=1;i<=len;++i)
            for(int j=1;j<=x.len;++j){
                res.a[i+j-1]+=a[i]*x.a[j];
                res.a[i+j]+=res.a[i+j-1]/base;
                res.a[i+j-1]%=base;
            }
        res.clear();
        return res;
    }
    HP operator /(const LL &x)const{//高精除低精 
        HP res;res.len=len;LL k=0ll;
        for(int i=len;i>0;--i){
            k=k*base+a[i];
            res.a[i]=k/x;
            k%=x;
        }
        res.clear();
        return res;
    }
    LL operator %(const LL &x)const{//高精模低精 
        LL k=0ll;
        for(int i=len;i>0;--i){
            k=k*base+a[i];
            k%=x;
        }
        return k;//其实跟高精除低精差不多的,只不过不用存res,返回的是k 
    }
    bool operator <(const HP &x)const{//重载小于号
        if(len==x.len){ 
            int i;
            for(i=len;a[i]==x.a[i]&&i>1;--i);
            if(i>=1) return a[i]<x.a[i];
            else return false;
        }
        else return len<x.len;
    }
    bool operator >(const HP &x)const{//重载大于号 
        if(len==x.len){ 
            int i;
            for(i=len;a[i]==x.a[i]&&i>1;--i);
            if(i>=1) return a[i]>x.a[i];
            else return false;
        }
        else return len>x.len;
    }
    //其实一般大于和小于号重载一个就够用了 
};
HP max(const HP &a,const HP &b){
    if(a>b) return a;
    return b;
}
HP min(const HP &a,const HP &b){
    if(a<b) return a;
    return b;
}
HP a,b,ans;
string sa,sb;
int main()
{
    cin>>sa>>sb;
    a=sa,b=sb;
    ans=a*b;
    ans.print();
    return 0; 
}
View Code

这里是压的4位,可以改B和base变成压B位。

想粘板子就粘吧,打得丑我承认。


Update on 11.3

在打模拟题时我自己就发现了几个bug:

(1)重载赋值运算符时,需要特判x=0的情况,不然无法赋值为0

(2)不能用unsigned long long,因为高精减时一旦减出负数unsigned long long就会溢出,然后就出锅了(我太菜了这都没发现)。。。因此最好用long long

(3)高精除与高精模,当除数很大时(接近long long的极大值)很容易溢出,这时运算过程中可能出现的最大位数为(压的位数+除数的位数),因此这个时候就应酌情减少压的位数了

以上bug直接导致我那题100->75。。。

2018-10-20

原文地址:https://www.cnblogs.com/gosick/p/9822197.html