看到P1932时就想写高精度了,
但是因为种种原因(单纯因为懒和废)一直没有写.
因为实践课的附加题需要用到高精度,
于是就开始了漫长的爆肝之路(前前后后肝了接近五天).
主要参考: https://www.luogu.com.cn/blog/user24922/solution-p1932
增加了对负数运算的支持, 并且修复了一个bug.
因为代码比较长, 为了控制篇幅, 正文基本不会贴代码.
大整数的存储结构
很显然, 自带的数据类型并不够用, 所以我们需要自己造一个新类型.
既然支持负数运算, 那么一个标志位自然是必不可少的.
接着就需要存数据了, 显然, 有了标志位, 直接存绝对值会更方便,
所以我们直接开个 long long 数组(用有符号的便于加减法运算)来存就好了.
操作符重载
输入输出流
因为我不会, 所以照抄的. 因为重载得放在结构体后面,
所以添加了一个函数(print())方便debug. ### 赋值 思路类似,
但是加上了正负号的处理, 同时做了一些小调整(为了保证能够完全读入,
所以牺牲一些效率放宽了读入的访问范围. 这里还能卡常不成?) ###
比较 因为考虑了正负数, 所以代码量直接翻倍. 基本思路如下: 首先考虑符号,
然后考虑位数, 最后逐位判断. 同时因为有符号,
所以添加了一个函数(cmp_abs())来进行绝对值的比较. ### 加减
因为只能使用已经重载过的运算符,
所以加法(先重载的加法)中就必须考虑正负了, 所以对每种情况都需要处理.
重载减法时因为加法是可用的, 所以对一些情况直接转换成加法即可. ### 乘法
模拟乘法竖式即可, 基本照抄. 添加了对正负号的处理:
同号或者一个为0显然结果是非负数, 异号显然结果是负数. ### 移位
原版中移位是有bug的, 当num不为1时得出的不一定是正确结果,
于是简单粗暴的套了一层num次的循环解决问题. ### 除法 原理是减法,
通过倍增优化, 主要还是增加了对正负号的处理, 同上. ### ~膜~模
有了乘除, 怎么实现不需要多说(~其实是懒得写了~)吧.
踩的坑
首先是操作符重载过程中对 +=, -=之类的处理,
操作符重载中的*this对应的是左操作数. 1 2 3 4 5 6 7 8 9
| BIGNUM operator += (const BIGNUM &a){ *this = *this + a; return *this; }
BIGNUM operator += (const BIGNUM &a){ return *this + a; }
|
然后是操作符重载过程中对*this的处理, 某些操作中直接修改了*this的内容,
也就是说修改了操作数, 导致后续操作结果错误. 不明白的话举个例子就好了,
这就相当于执行完 "c = a + b; " 之后, a 或者 b 的值发生了改变.
还有一些就是逻辑错误了, 这个没什么好说的, 纯属脑子不清醒.
奇奇怪怪的优化
读原版代码的时候发现了一些神奇的语句: 1 2 3 4
| s[s[0]] += (num[j] ^ 48) * w;
w = (w << 1) + (w << 3);
|
还有通过倍增实现
O(n) 到 O(log2(n))
的除法, 优化效果显著.
完整代码
更新记录(2021.04.11): + 修改输出流重载部分,
解决关闭同步后可能出现的问题. + 微调部分语句, 增加可读性.
点我查看完整代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376
| #include <algorithm> #include <cstdio> #include <cstring> #include <iostream> #include <iomanip>
const int LEN = 10009; using namespace std; typedef long long ll;
struct BIGNUM{ static const int BIT = 9; static const int MOD = 1e9; bool flag; ll s[LEN]; BIGNUM(){ flag = 0; memset(s, 0, sizeof(s)); s[0] = 1; } void init(){ memset(s, 0, sizeof(s)); } void print(){ BIGNUM a = *this; if (a.flag){ printf("-"); } printf("%lld", a.s[a.s[0]]); for (int i = a.s[0] -1; i > 0; i--){ printf("%09lld", a.s[i]); } printf("\n"); } BIGNUM operator = (const char *num){ if (num[0] == '-'){ flag = 1; num++; } else if (num[0] == '+'){ flag = 0; num++; } int l = strlen(num); s[0] = 0; for (int i = l - 1; i > -9; i -= 9){ int temp = 0; for (int j = i - 8; j <= i; j++){ if (j < 0) continue; else{ temp = (temp<<1) + (temp<<3); temp += num[j] ^ 48; } } s[++s[0]] = temp; } while(!s[s[0]] && s[0] > 1) s[0]--; return *this; } BIGNUM operator = (const int num) { char a[LEN]; sprintf(a, "%d", num); *this = a; return *this; } BIGNUM(int num) { *this = num; } BIGNUM(const char *num) { *this = num; } bool operator < (const BIGNUM &a){ if (flag > a.flag) return 1; else if (flag < a.flag) return 0; else{ if ((s[0] < a.s[0] && (!flag)) || (s[0] > a.s[0] && flag)) return 1; else if (s[0] != a.s[0]) return 0; else{ for (int i = s[0]; i > 0; i--){ if (s[i] < a.s[i]){ if (flag) return 0; else return 1; } else if (s[i] > a.s[i]){ if (flag) return 1; else return 0; } } return 0; } } } bool operator > (const BIGNUM &a){ if (flag > a.flag) return 0; else if (flag < a.flag) return 1; else{ if ((s[0] > a.s[0] && (!flag)) || (s[0] < a.s[0] && flag)) return 1; else if (s[0] != a.s[0]){ return 0; } else{ for (int i = s[0]; i > 0; i--){ if (s[i] > a.s[i]){ if (flag) return 0; else return 1; } else if (s[i] < a.s[i]){ if (flag) return 1; else return 0; } } return 0; } } } bool operator == (const BIGNUM &a){ if (flag != a.flag) return 0; else{ if (s[0] != a.s[0]) return 0; else{ for (int i = s[0]; i > 0; i--){ if (s[i] != a.s[i]) return 0; } return 1; } } } bool operator >= (const BIGNUM &a){ if(*this > a || *this == a) return 1; return 0; } bool operator <= (const BIGNUM &a){ return (*this == a)||(*this < a); } int cmp_abs(const BIGNUM &a){ if (s[0] > a.s[0]) return 1; else if (s[0] < a.s[0]) return -1; else{ for (int i = s[0]; i > 0; i--){ if (s[i] > a.s[i]) return 1; else if (s[i] < a.s[i]) return -1; } } return 0; } BIGNUM operator + (const BIGNUM &B){ BIGNUM c, a = *this, b = B; if (!(a.flag || b.flag) || (a.flag && b.flag)){ c.flag = a.flag; int x = 0; c.s[0] = max(a.s[0], b.s[0]) + 1; for(int i = 1; i <= c.s[0]; i++) { c.s[i] = a.s[i] + b.s[i] + x; x = c.s[i] / MOD; c.s[i] %= MOD; } } else if (a.flag){ if (a.cmp_abs(b) == 1){ c.flag = 1; c.s[0] = max(a.s[0], b.s[0]) + 1; for(int i = 1; i <= c.s[0]; i++) { c.s[i] = a.s[i] - b.s[i]; if (c.s[i] < 0){ c.s[i] += MOD; a.s[i + 1]--; } } } else{ c.flag = 0; c.s[0] = max(a.s[0], b.s[0]) + 1; for(int i = 1; i <= c.s[0]; i++) { c.s[i] = b.s[i] - a.s[i]; if (c.s[i] < 0){ c.s[i] += MOD; b.s[i + 1]--; } } } } else{ if (b.cmp_abs(a) == 1){ c.flag = 1; c.s[0] = max(a.s[0], b.s[0]) + 1; for(int i = 1; i <= c.s[0]; i++) { c.s[i] = b.s[i] - a.s[i]; if (c.s[i] < 0){ c.s[i] += MOD; b.s[i + 1]--; } } } else{ c.flag = 0; c.s[0] = max(a.s[0], b.s[0]) + 1; for(int i = 1; i <= c.s[0]; i++) { c.s[i] = a.s[i] - b.s[i]; if (c.s[i] < 0){ c.s[i] += MOD; a.s[i + 1]--; } } } } while(c.s[c.s[0]] == 0 && c.s[0] > 1) c.s[0]--; return c; } BIGNUM operator += (const BIGNUM &a){ *this = *this + a; return *this; } BIGNUM operator - (const BIGNUM &B){ BIGNUM c, a = *this, b = B; if ((!a.flag) && (!b.flag)){ if (a >= b){ c.flag = 0; c.s[0] = max(a.s[0], b.s[0]) + 1; for(int i = 1; i <= c.s[0]; i++) { c.s[i] = a.s[i] - b.s[i]; if (c.s[i] < 0){ c.s[i] += MOD; a.s[i + 1]--; } } } else{ c.flag = 1; c.s[0] = max(a.s[0], b.s[0]) + 1; for(int i = 1; i <= c.s[0]; i++) { c.s[i] = b.s[i] - a.s[i]; if (c.s[i] < 0){ c.s[i] += MOD; b.s[i + 1]--; } } } } else{ b.flag = !b.flag; c = a + b; } while(c.s[c.s[0]] == 0 && c.s[0] > 1) c.s[0]--; return c; } BIGNUM operator -= (const BIGNUM &a){ *this = *this - a; return *this; } BIGNUM operator * (const BIGNUM &B){ BIGNUM c, a = *this, b = B; c.init(); if (!(a.flag || b.flag) || (a.flag && b.flag)) c.flag = 0; else{ if ((a.s[0] == 1 && a.s[1] == 0) || (b.s[0] == 1 && b.s[1] == 0)) c = 0; else c.flag = 1; } c.s[0] = a.s[0] + b.s[0]; for(int i = 1; i <= a.s[0]; i++) { int x = 0; for(int j = 1; j <= b.s[0]; j++) { c.s[i + j - 1] += a.s[i] * b.s[j] + x; x = c.s[i + j - 1] / MOD; c.s[i + j - 1] %= MOD; } c.s[i + b.s[0]] = x; } while (c.s[c.s[0]] > 0) c.s[0]++; while (c.s[c.s[0]] == 0 && c.s[0] > 1) c.s[0]--; return c; } BIGNUM operator *= (const BIGNUM &a){ *this = *this * a; return *this; } BIGNUM operator << (const int &a){ for (int i = 0; i < a; i++){ s[0]++; for (int j = s[0]; j > 0; j--){ s[j] <<= 1; } for (int j = 1; j < s[0]; j++){ if (s[j] >= MOD){ s[j] -= MOD; s[j + 1]++; } } while(s[s[0]] == 0 && s[0] > 1) s[0]--; } return *this; } BIGNUM operator <<= (const int &a){ *this = *this << a; return *this; } BIGNUM operator >> (const int &a){ for (int i = 0; i < a; i++){ for (int j = s[0]; j > 0; j--){ if ((s[j]&1) && j != 1) s[j - 1] += MOD; s[j] >>= 1; } } while(s[s[0]] == 0 && s[0] > 1) s[0]--; return *this; } BIGNUM operator >>= (const int &a){ *this = *this >> a; return *this; } BIGNUM operator / (const BIGNUM &c){ BIGNUM a = *this, b = c, temp, ans; ans.init(); temp.init(); ans = 0; temp = 1; bool sign; if (!(a.flag || b.flag) || (a.flag && b.flag) || cmp_abs(c) == -1) sign = 0; else sign = 1; a.flag = b.flag = 0; while (a >= b){ b <<= 1; temp <<= 1; } while (temp.s[0] > 1 || temp.s[1]){ if (a >= b){ a -= b; ans += temp; } b >>= 1; temp >>= 1; } while (!ans.s[ans.s[0]] && ans.s[0] > 1) ans.s[0]--; ans.flag = sign; return ans; } BIGNUM operator /= (const BIGNUM &a){ *this = *this / a; return *this; } BIGNUM operator % (const BIGNUM &a){ BIGNUM c = *this / a, d = c * a; return *this - d; } BIGNUM operator %= (const BIGNUM &a){ *this = *this % a; return *this; } }; ostream& operator << (ostream &out, const BIGNUM &a) { if (a.flag){ out << '-'; } out << a.s[a.s[0]]; for(int i = a.s[0] - 1; i >= 1; i--) out << fixed << setfill('0') << setw(9) << a.s[i]; return out; } istream& operator >> (istream &in, BIGNUM &a) { char str[LEN]; in >> str; a = str; return in; }
int main(){ BIGNUM a, b; cin>>a>>b; cout<<a + b<<'\n'; cout<<a - b<<'\n'; cout<<a * b<<'\n'; cout<<a / b<<'\n'; cout<<a % b<<'\n'; return 0; }
|