Loading

在线 $\Theta(1)$ 逆元

// 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]);
	}
}
posted @ 2023-05-17 22:29  Qiuly  阅读(462)  评论(2编辑  收藏  举报