在线O(1)求逆元

怎么还有厉害的在线O(1)求逆元,不过常数确实有点儿太大了

本文大部分搬运于这里

相信大家都做过 POJ2478 这道题吧,这道题的 Farey 序列 Fn 包含了分子分母不大于 n 且互质的数。该分数可以为 01

嗯我们现在要把 Fp3求出来,然后有一个妙妙定理,就可以在线地 O(1) 求逆元。

定理:给定一个分数 ab(0ab1)和一个 n(n>1),你都可以在 Fn1 中找到一个分数使得 |abxy|1yn,并且这个分数一定是 ab 在序列中使用 useful binary search 后往前或往后的第一个数。

证明?注意到 Fn1 实际上包含了分子分母都不大于 n 的所有分数,只不过将其去重了。所以我们对每一个分母都讨论一遍。

差不多就是要证明对于所有不大于 n 的数对 (x,y)[xy1ny,xy+1ny] 的并包含了 [0,1]。也就是对于任意一个区间,一定有另一个在其右侧区间与其相交。

通分:[nx1ny,nx+1ny]

那么我们接下来需要证明对于 x1<y1 一定有 x2<y2 满足 x1y1x2y2nx1+1ny1>nx21ny2

还是通分:nx1y2+y2>nx2y1y1,也就是 y1+y2>n(x2y1x1y2)。很明显一定有 x2=x11,y2=y11 满足条件。

啥?x1=0,y1=1?右端点为 1n,那么存在 1n1 满足 1n11n(n1)=1n

所以自然就是二分查找后向前或向后的第一个数。

以上内容均为口胡

我们发现这个结论 |abxy|1yn,通分之后就是 |aybx|bn。这里把 bp 替换一下。

上面的结论相当于 ayt(modp),且 tpn,相当于 a1yt1(modp)

所以只需要找到这个分数之后,处理 t 的逆元就好了啊。

因为 tpn,我们预处理一个长度为 n 的 Farey 肯定需要至少 O(n2) 的时间,n2=pn,得到 n=p3。所以需要预处理 Fp3p23 以内的数的逆元。

那么如何 O(n2) 预处理 Farey 序列和 O(1) 二分查找?

我们可以请教神圣二分帝国皇帝 Um_nik

对于 Farey 序列中的任意两个数 x1y1x2y2,有 x1y1x2y2=x1y2x2y1y1y2。在分母最大的情况下不大于 p23,分子最小不小于 1,所以只需要将原分数乘上 p23 后向下取整,一定互不相同。

所以只需要把 xy 映射到 x×p23y 后桶排序就可以啦。

代码压过行,还卡过常,实际上写起来也不是很长。

目测常数是正常离线求逆的 23 倍。

#include<iostream>
#include<cctype>
#include<cmath>
typedef unsigned ui;
typedef __uint128_t L;
typedef unsigned long long ull;
const ui M1=1000,M2=M1*M1;
ui T,m1,m2,MOD,len,fra[M2+5],inv[M2+5],sum[M2+5];
ui q[M2+5];ull p[M2+5];
double invm1,INV;
char buf[1<<22|1],*p1=buf;
inline char Getchar(){
	return*p1=='\0'&&(std::cin.read(p1=buf,1<<22)),*p1++;
}
struct FastMod{
	ull b,m;
	FastMod(ull b):b(b),m(ull((L(1)<<64)/b)){}
	friend inline ull operator%(const ull&a,const FastMod&mod){
		ull r=a-(L(mod.m)*a>>64)*mod.b;
		return r>=mod.b?r-mod.b:r;
	}
}mod(2);
inline ull abs(const ull&a){
	return a>>63?-a:a;
}
inline ui read(){
	ui n(0);char s;
	while(!isdigit(s=Getchar()));
	while(n=n*10+(s&15),isdigit(s=Getchar()));
	return n;
}
inline void init(){
	ui i,j,x;
	for(i=1;i^m1;++i){
		const double&INV=1./i+1e-15;
		for(j=0;j^i;++j)if(!sum[x=1ull*j*m2*INV])sum[x]=1,fra[x]=m1*i+j;
	}
	for(i=0;i<=m2;++i){
		if(sum[i])++len,q[len]=fra[i]*invm1,p[len]=1ull*(fra[i]-q[len]*m1)*MOD;
		if(i)sum[i]+=sum[i-1];inv[i]=i>1?1ull*(MOD-MOD/i)*inv[MOD%i]%mod:i;
	}
}
inline ui Inv(const ui&a){
	static ui q,k;static ull t;
	if(a<=m2)return inv[a];if(MOD-a<=m2)return MOD-inv[MOD-a];k=sum[ui(a*INV)];
	if(k<=len){
		q=::q[k];t=1ull*a*q-p[k];
		if(abs(t)<=m2)return 1ull*q*(t>>63?MOD-inv[-t]:inv[t])%mod;
	}
	if(++k<=len){
		q=::q[k];t=1ull*a*q-p[k];
		if(abs(t)<=m2)return 1ull*q*(t>>63?MOD-inv[-t]:inv[t])%mod;
	}
	return-1;
}
signed main(){
	std::ios::sync_with_stdio(false);std::cin.tie(0);std::cout.tie(0);
	ui k,x,sum(0);T=read();MOD=read();mod=FastMod(MOD);x=k=read();
	m1=pow(MOD,1./3)+1;m2=m1*m1;invm1=1./m1+1e-15;INV=1.*m2/MOD+1e-15;init();
	while(T--)sum=(sum+1ull*x*Inv(read()))%mod,x=1ull*x*k%mod;std::cout<<sum;
}
posted @   Prean  阅读(1296)  评论(2编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
var canShowAdsense=function(){return !!0};
点击右上角即可分享
微信分享提示