2023.1.31 闲话

我看差不多时机到了,可以发闲话了 .

plate_let 提出的一个模乘,好写且跑得飞快 .

原文:https://www.luogu.com.cn/blog/plateIet/modulo .

目标:对于定值 \(x,m\),多组询问每次给一个 \(a\)\(ax\bmod m\) .

\(\{x\}=x-\lfloor x\rfloor\)(小数部分),则

\[ax\bmod m=\left\{a\cdot\dfrac xm\right\}\cdot m \]

对于整数 \(p\),令 \(\dfrac xm\approx\dfrac p{2^{\omega}}\),其中 \(\omega=64\),则

\[\begin{aligned}ax\bmod m&\approx\left\{a\cdot\dfrac p{2^{\omega}}\right\}\cdot m\\&=\dfrac{ap\bmod 2^{\omega}}{2^{\omega}}\cdot m\\&=\dfrac{ap\bmod 2^{\omega}\times m}{2^{\omega}}\end{aligned} \]

对于估计 \(\dfrac xm\approx\dfrac p{2^{\omega}}\),取最近的整数 \(p\) 使得 \(\dfrac xm\le\dfrac p{2^{\omega}}\),最后的除法向下取整,可以证明结果是准确的 .

证明:

目标:取 \(p=\left\lceil\dfrac xm\cdot 2^{\omega}\right\rceil\),答案向下取整时,对于 \(a\le\dfrac{2^{\omega}}m\) 计算结果准确 .

\(\dfrac p{2^{\omega}}=\dfrac xm+\varepsilon\),其中 \(\varepsilon\in\left[0,\dfrac1{2^{\omega}}\right)\) .

于是

\[\begin{aligned}\left\lfloor\left\{a\cdot\dfrac p{2^\omega}\right\}\cdot m\right\rfloor&=\left\lfloor\left\{a\cdot\left(\dfrac xm+\varepsilon\right)\right\}\cdot m\right\rfloor\\&=\left\lfloor\left\{a\cdot\dfrac xm+a\varepsilon\right\}\cdot m\right\rfloor\\&=\left\lfloor\left\{a\cdot\dfrac xm\right\}+a\varepsilon\cdot m\right\rfloor\\&=\left\lfloor\left\{a\cdot\dfrac xm\right\}m+am\varepsilon\right\rfloor\end{aligned} \]

保证没有问题的条件就是 \(am\varepsilon<1\),因为 \(a\le\dfrac{2^{\omega}}m\) 所以肯定对 .

证毕 .

也可以在第二个等号的结果后面直接感性理解得 \(a\varepsilon<\dfrac 1m\),结果相同 .

plate_let 的实现:

const int P = 998244353;

void calc(int n, int k, int a[]) {
    unsigned long long p = (((unsigned __int128)k << 64) + P - 1) / P;
    for(int i = 0; i < n; i++)
        a[i] = (unsigned)a[i] * p * (unsigned __int128)P >> 64;
}

这份代码中具体的卡常技巧可以看原文,这里就不再说一遍了 .

终 .

posted @ 2023-01-31 09:42  Jijidawang  阅读(98)  评论(1编辑  收藏  举报
😅​