2023.1.31 闲话
我看差不多时机到了,可以发闲话了 .
plate_let 提出的一个模乘,好写且跑得飞快 .
原文:https://www.luogu.com.cn/blog/plateIet/modulo .
目标:对于定值 \(x,m\),多组询问每次给一个 \(a\) 求 \(ax\bmod m\) .
令 \(\{x\}=x-\lfloor x\rfloor\)(小数部分),则
对于整数 \(p\),令 \(\dfrac xm\approx\dfrac p{2^{\omega}}\),其中 \(\omega=64\),则
对于估计 \(\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)\) .
于是
保证没有问题的条件就是 \(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;
}
这份代码中具体的卡常技巧可以看原文,这里就不再说一遍了 .
终 .
以下是博客签名,正文无关
本文来自博客园,作者:Jijidawang,转载请注明原文链接:https://www.cnblogs.com/CDOI-24374/p/17071803.html
版权声明:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议(CC BY-NC-SA 4.0)进行许可。看完如果觉得有用请点个赞吧 QwQ