// created on 23.04.30
目录在线 O(1) 逆元
给定 质数 模数 \(p\),多次查询一个数 \(a\) 的逆元,强制在线。
我们有一个 \(O(p^{\frac{2}{3}})\) 的预处理,\(O(1)\) 查询的做法。
Farey 序列即 \(F_n\) 表示所有分母不超过 \(n\) 的最简真分数构成的有序数列(左右端点为 \(\frac{0}{1}\)、\(\frac{1}{1}\))。求 Farey 序列是简单,每次只要用 \(\frac{a}{b},\frac{c}{d}\) 生成 \(\frac{a+c}{b+d}\) 并放在中间即可。Farey 序列有一个性质是 \(cb-ad=1\),这个容易证明。
定理:对于任意整数 \(n\geq 2\) 和任意实数 \(v\in[0,1]\),总能在 \(F_{n-1}\) 找到 \(\frac{x}{y}\) 满足 \(|v-\frac{x}{y}|\leq \frac{1}{yn}\) 。更强地,这个 \(\frac{x}{y}\) 一定是 \(v\) 向前 / 向后的第一个分数。
于是,令 \(n\) 为定值。对于 \(a\) 令 \(v=\frac{a}{p}\) 并找到 \(\frac{x}{y}\) 满足 \(|\frac{a}{p}-\frac{x}{y}|\leq \frac{1}{yn}\) 。即 \(|ay-px|\leq \lfloor\frac{p}{n}\rfloor\) 。意思是 \(ay\equiv u\pmod{p}\),而 \(|u|\leq \lfloor\frac{p}{n}\rfloor\) 。注意到 \(a^{-1}\equiv u^{-1}y\pmod{p}\),因此只要处理 \(O(\lfloor\frac{p}{n}\rfloor)\) 个数的逆元。
注意 \(F_{n-1}\) 中,\(\lfloor\frac{xn^2}{y}\rfloor\) 各不相同。因此开一个长度为 \(n^2\) 的 01 序列,记录某个 \(\lfloor\frac{xn^2}{y}\rfloor\) 是否存在。枚举 \(x,y\),记录每个位置的分数即可。而找 \(\frac{x}{y}\),则相当于查前后的第一个 \(1\),只要算前缀即可。
最后取 \(n=p^{\frac{1}{3}}\) 即可。
namespace o1_inv {
const int n = 1e3, n2 = 1e6, lim = mod / n;
const int N = n2 + 5;
int sum[N], _inv[N], siz;
pair <int, int> frac[N], farey[N];
inline void init () {
lep (q, 1, n - 1) lep (p, 0, q) {
int i = p * n2 / q;
if (! sum[i]) sum[i] = 1, frac[i] = { p, q };
}
lep (i, 1, n2) sum[i] += sum[i - 1];
lep (i, 0, n2) if (frac[i].se) farey[siz ++ ] = frac[i];
_inv[1] = 1;
lep (i, 2, lim) _inv[i] = mul (mod - mod / i, _inv[mod % i]);
}
inline int inv (int a) {
int i = 1ll * a * n2 / mod, k = sum[i], p, q; ll t;
if (k < siz) {
tie (p, q) = farey[k], t = 1ll * a * q - 1ll * mod * p;
if (abs (t) <= lim) return mul (q, (t > 0 ? _inv[t] : mod - _inv[ - t]));
}
if ( -- k >= 0) {
tie (p, q) = farey[k], t = 1ll * a * q - 1ll * mod * p;
if (abs (t) <= lim) return mul (q, (t > 0 ? _inv[t] : mod - _inv[ - t]));
}
return -1;
}
}
再记一个做法:一次询问时,我们希望找到 \(u\) 使得 \(xu\bmod p\) 较小,这样只要回答 \(\frac{1}{xu}\) 。
考虑令 \(x=at+b\),而 \(t=p^{\frac{1}{3}},u\leq p^{\frac{1}{3}}\),则 \(bu\) 只有 \(p^{\frac{2}{3}}\) 。
只要达成 \(-p^{\frac{2}{3}}\leq|atu|\leq p^{\frac{2}{3}}\) 就可以了。即找到 \(\frac{v}{u}\equiv at\pmod {p}\) 且 \(|v|\leq p^{\frac{2}{3}}\) 。此时 \(\{uat-v\}\) 的个数已经超过了 \(p\),在这之中一定能找到 \(0\)(要不然存在俩相同的,相减就是 \(0\) 了)。
于是只要找到 \(u\) 就大功告成了。枚举 \(u\) 找所有 \(-p^{\frac{2}{3}}\leq|atu|\leq p^{\frac{2}{3}}\) 的 \(a\),因为 \(tu\geq p^{\frac{1}{3}}\),所以暴力找出 \(a\) 即可。
原文:在线 \(\Theta(1)\) 逆元 - zhoukangyang - 博客园 (cnblogs.com) 。
namespace o1_inv {
const int n = 1 << 10, N = (1 << 21) | 1;
int pool[N << 1], * iv = pool + N;
int rec[N], pot[N];
void init (int _p) {
int lim = mod >> 10, _lim = mod - lim;
lep (u, 1, n) {
int cur = 0, d = u << 10;
for (int i = 0; i <= lim; ) {
if (cur <= lim) rec[i] = u;
else if (cur >= _lim) rec[i] = - u;
else {
int r = (_lim - cur - 1) / d;
cur += r * d, i += r;
}
if (cur += d, i ++, cur >= mod) cur -= mod;
}
}
lep (a, 0, lim) pot[a] = mul (a << 10, rec[a] + mod);
iv[1] = 1;
lep (i, 2, N - 1) iv[i] = mul (iv[mod % i], mod - mod / i);
lep (i, 1, N - 1) iv[ - i] = mod - iv[i];
}
int inv (int x) {
int a = x >> 10, b = x & 1023, u = rec[a];
return mul (u + mod, iv[pot[a] + b * u]);
}
}
标签:frac,在线,int,inv,lep,leq,逆元,Theta,mod
From: https://www.cnblogs.com/qiulyqwq/p/17410552.html