一种基于值域的 $O(1)\ gcd$

前言

建议不要看本文,纯个人记录,你学不到什么的,。,,,,,
本文介绍了一种基于值域的 \(O(1) \ gcd\) (大部分描述都是贺的。本科技通常都是用来卡常(但是大部分题都不卡 \(gcd\) 带的 \(log\),所以本文没什么用。

正文

\[Th.1:可将 \ x \ 分解为 \{a,b,c\},满足 \ a,b,c\le \sqrt n\ or \ x \in Prime \]

\[Th.2:对于询问 \ gcd(x,y),分别考虑a,b,c对答案的贡献,a对答案的贡献为gcd(a,y),再将y除以 gcd(a,y),再对b,c做相同处理,三个贡献相乘即为gcd(x,y) \]

\[Th.3: 对于x\ge 2,找到其最小质因子 p以及合法的 \frac{x}{p}的分解\{a,b,c\},那么 x的合法分解即为 \{ap,b,c\} \]

证明见上文链接。
对于实现部分,由定理3可以线性筛预处理每个数最小质因子,由定理1,2可直接预处理 \(\sqrt n\) 以内的 \(gcd\),最后直接计算贡献即可。

代码

namespace Qgcd{
	bool vis[V+2];
	int pre[B+1][B+1],fac[V+3][3],prime[V],tot;
	void init(){
		fac[1][0]=1,fac[1][1]=1,fac[1][2]=1;
		for(int i=2;i<=V;i++){
			if(!vis[i])
				prime[++tot]=i,fac[i][0]=fac[i][1]=1,fac[i][2]=i;
			for(int j=1,tmp;prime[j]<=V/i;j++){
				tmp=i*prime[j];
				vis[tmp]=1;
				fac[tmp][0]=fac[i][0]*prime[j];
				fac[tmp][1]=fac[i][1];
				fac[tmp][2]=fac[i][2];
				if(fac[tmp][0]>fac[tmp][1])
					swap(fac[tmp][0],fac[tmp][1]);
				if(fac[tmp][1]>fac[tmp][2])
					swap(fac[tmp][1],fac[tmp][2]);	
				if(i%prime[j]==0) 
					break;
			}
		}
		for(int i=0;i<=B;i++)
			pre[i][0]=pre[0][i]=i;
		for(int i=1;i<=B;i++){
			for(int j=1;j<=i;j++){
				pre[i][j]=pre[j][i]=pre[j][i%j];
			}
		}
	}
	int gcd(int a,int b){
		int ans=1,tmp;
		for(int i=0;i<3;i++){
			if(fac[a][i]>B){
				if(b%fac[a][i]==0) tmp=fac[a][i];
				else tmp=1;
			}
			else tmp=pre[fac[a][i]][b%fac[a][i]];
			ans*=tmp,b/=tmp;
		}
		return ans;
	}
};

posted @ 2024-07-29 20:40  SmileMask  阅读(14)  评论(0编辑  收藏  举报