高精度模板【重载运算符+压位】
昨天做一道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; }
这里是压的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
愿你有一天能和重要的人重逢