【洛谷P5435】基于值域预处理的快速 GCD

题目

题目链接:https://www.luogu.com.cn/problem/P5435
给定 \(n\) 个正整数 \(a_1,a_2,\dots,a_n\),再给定 \(n\) 个正整数\(b_1,b_2,\dots,b_n\),你需要对每对 \((i,j)\) 求出 \(a_i\)\(b_j\) 的最大公因数。
不难发现你的输出应有 \(n^2\) 个正整数。为了减少输出对程序的运行效率的影响,你只需要输出 \(n\) 行,每行一个整数 \(A_i\)
其中对于 \(i\in[1,n]\)\(A_i=\sum_{j=1}^{n}i^j\gcd(a_i,b_j)\)。由于答案可能过大,你只需要输出模 \(998,244,353\) 后的结果即可。
\(n\leq 5000\)\(1\leq a_i\leq 10^6\)

思路

一种挺有趣的科技。
对于任意一个数 \(x\),肯定有一种方法可以把 \(x\) 分解成 \(x_1\times x_2\times x_3\),且 \(x_1,x_2,x_3\)\(\leq \sqrt{x}\) 或是质数。
具体的构造方法,我们可以线性筛出值域内所有数的最小质因子。对于一个数 \(p\),如果它是质数,显然可以分解为 \(1\times 1\times p\);如果它不是质数,设它的最小质因子是 \(d\),且 \(q=\frac{p}{d}\) 分解为了 \(q_1\times q_2\times q_3\)\(q_1\leq q_2\leq q_3\)),那么可以把 \(p\) 分解为 \(q_1\times d,q_2,q_3\)
证明:

  • 如果 \(d\leq \sqrt[4]{p}\),因为 \(q_1\leq \sqrt[3]{\frac{p}{d}}\),所以 \(d\times q_1\leq d\times \sqrt[3]{\frac{p}{d}}=p^{\frac{1}{3}}\times d^{\frac{2}{3}}\)。而 \(\sqrt{p}\div p^{\frac{1}{3}}=p^{\frac{1}{6}}\)\(d^{\frac{2}{3}}\leq \left(\sqrt[4]{p}\right)^{\frac{2}{3}}\leq p^{\frac{1}{6}}\)。故 \(d\times q_1\leq \sqrt{p}\)
  • 如果 \(d>\sqrt[4]{p}\)
    • 如果 \(q_1=1\),显然 \(1\times d\leq \sqrt{p}\)
    • 如果 \(q_1>1\),则 \(d\leq q_1\leq q_2\leq q_3\),则 \(d\times q_1\times q_2\times q_3>\left(\sqrt[4]{p}\right)^4=p\),矛盾。

这样把值域内所有的数分解好后,预处理出一个 \(\sqrt{v}\times \sqrt{v}\)\(\gcd\) 数组(\(v\) 是值域)。对于每一次询问 \(a,b\),把 \(a\) 分解成 \(a_1\times a_2\times a_3\)

  • 如果 \(a_1\) 是质数:如果 \(b\)\(a_1\) 的倍数返回 \(a_1\),否则返回 \(1\)
  • 如果 \(a_1\) 不是质数,\(\gcd(a_1,b)=\gcd(a_1,b\bmod a_1)\),此时两个数都不超过 \(\sqrt{v}\),直接返回预处理的值。

\(a_1,a_2,a_3\) 分别做一次乘起来,就是 \(\gcd(a,b)\) 了。注意每次 \(b\) 需要除掉返回的 \(\gcd\)
时间复杂度 \(O(v+n^2)\)

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

const int N=5010,M=1000010,MM=1010,MOD=998244353;
int n,m,a[N],b[N],prm[M],v[M],f[M][3],g[MM][MM];

void findprm(int n)
{
	v[1]=1;
	for (int i=2;i<=n;i++)
	{
		if (!v[i]) prm[++m]=i,v[i]=i;
		for (int j=1;j<=m;j++)
		{
			if (i>n/prm[j] || prm[j]>v[i]) break;
			v[i*prm[j]]=prm[j];
		}
	}
}

void prework()
{
	for (int i=1;i<MM;i++) g[0][i]=g[i][0]=i;
	for (int i=1;i<MM;i++)
		for (int j=1;j<=i;j++)
			g[i][j]=g[j][i]=g[j][i%j];
	for (int i=1;i<M;i++)
		if (v[i]==i)
			f[i][0]=1,f[i][1]=1,f[i][2]=i;
		else
		{
			f[i][0]=f[i/v[i]][0]*v[i]; f[i][1]=f[i/v[i]][1]; f[i][2]=f[i/v[i]][2];
			if (f[i][0]>f[i][1]) swap(f[i][0],f[i][1]);
			if (f[i][1]>f[i][2]) swap(f[i][1],f[i][2]);
		}
}

int gcd(int x,int &y)
{
	int d=(x>MM)?((y%x)?1:x):g[x][y%x];
	y/=d;
	return d;
}

int main()
{
	findprm(M-1);
	prework();
	scanf("%d",&n);
	for (int i=1;i<=n;i++) scanf("%d",&a[i]);
	for (int i=1;i<=n;i++) scanf("%d",&b[i]);
	for (int i=1;i<=n;i++)
	{
		ll ans=0,res=1;
		for (int j=1,k;j<=n;j++)
		{
			res=res*i%MOD; k=b[j];
			ans=(ans+res*gcd(f[a[i]][0],k)*gcd(f[a[i]][1],k)*gcd(f[a[i]][2],k))%MOD;
		}
		cout<<ans<<"\n";
	}
	return 0;
}
posted @ 2021-08-23 11:00  stoorz  阅读(77)  评论(0编辑  收藏  举报