一种防止爆 long long 的 trick,求原理

学术版

@[lzy20091001](/user/932039) 原理是 ```long double``` 存储位数极多,而这个函数返回值是 $ab-(\lfloor\frac{ab+0.5}{p}\rfloor\times p)$,本质就是 $ab\bmod p$,加上 $0.5$ 是因为 ```long double``` 计算时有精度误差,对结果没有影响。 在 $a,b,p$ 为 ```long long``` 情况下可以大胆使用,经过测试 ```long double``` 最多可以存储到 $260$ 个 ```long long``` 最大值的乘积。具体 ```long double``` 存储最大值为 $1.18973e+4932$。
by CaiZi @ 2024-03-30 20:18:16


原理是给 $ab/p$ 弄了个近似值 $d$,然后利用 $ab\equiv ab-dp\pmod p$,得到一个范围较小的结果 $c$。(大概和 $p$ 同级) 你可能期望它保证 $c$ 在 $[0,p)$ 以内,但这太苛刻了而且没必要,反正你可以在输出答案前取模一次来修正,结果都一样。 所以只要 $c$ 不爆 long long 这个就能跑。 具体分析要考虑 long double 的精度。 注意这个**和 long double 范围没关系**,看的是有效位数。 一般来说 long double 至少有 80 位。再怎么样也能保证 $\dfrac1{2^{70}}$ 以内的相对误差了,那么 $c<\dfrac{ab}{2^{70}}$ 这显然不会爆。 所以结论是一定能跑。
by XeCtera @ 2024-03-30 20:31:26


@[CaiZi](/user/728853) 那请问 $0.5$ 是必须的吗,自己测了一组 $a = b = 10 ^ {18}, c = 10 ^ {18} + 7$,加和不加 $0.5$ 算出的都是正确答案 $49$。
by lzy20091001 @ 2024-03-30 20:32:45


@[lzy20091001](/user/932039) 只是为了保险,因为 ```long double``` 精度容易丢失,导致最终结果少 $1$ 或多 $1$。
by CaiZi @ 2024-03-30 20:34:36


哦对,也不一定就满足 $c<\dfrac{ab}{2^{70}}$ ,实际上也许只能说 $c<\dfrac{ab}{2^{70}}+p$ 。 所以对 $p$ 还是有些要求的。但只要别像 $p=2^{63}-1$ 这么极端就行。
by XeCtera @ 2024-03-30 20:35:01


@[lzy20091001](/user/932039) 这玩意好像老早之前就有了,叫光速乘,但现在 CCF 允许 `__int128` 所以光速乘没啥用了
by x383494 @ 2024-03-30 21:52:51


@[lzy20091001](/user/932039) 这是对的吗?在计算 c 的时候都有 a * b 了,为啥不直接 a * b % p
by Iniaugoty @ 2024-03-31 22:07:48


@[Iniaugoty](/user/768612) 一开始我就是这个疑问,自己实测 $a = b = 10 ^ {18}$ 没有问题,虽然我还是没太懂原理但我决定就这么用了
by lzy20091001 @ 2024-03-31 22:11:02


|