P5655 基础数论函数练习题 题解

chen_03 Orz!

题意

给定长度为 \(n\) 的数组 \(a\)\(Q\) 次询问 \(\operatorname{lcm}\left(a_l, a_{l + 1}, \ldots , a_{r - 1}, a_r\right)\),对 \({10}^9 + 7\) 取模。

题解

对于每个右端点,我们维护每个左端点的答案。

具体的,设当前右端点为 \(m\),我们尝试找到数列 \(b_i\) 使得区间 \([i,m]\) 的答案为 \(\prod_{j=i}^{m}b_j\),显然 \(b_{i}|a_{i}\)

假设我们已求出右端点为 \(m-1\) 时的数组 \(b\),现在尝试求出右端点为 \(m\) 时的数组 \(b'\)

首先,\(b'_m=a_m\)。若 \(j\gt i\)\(b'_j\) 都已求出,那么有:

\[\prod_{j=i+1}^{m}b'_{j}=\frac{a_{m}\prod_{j=i+1}^{m-1}b_j}{\gcd\left(a_{m},\prod_{j=i+1}^{m-1}b_j\right)} \]

\[\prod_{j=i}^{m}b'_{j}=\frac{a_{m}\prod_{j=i}^{m-1}b_j}{\gcd\left(a_{m},\prod_{j=i}^{m-1}b_j\right)} \]

两式相除得:

\[b'_{i}=\frac{b_i}{\frac{\gcd\left(a_{m},\prod_{j=i}^{m-1}b_j\right)}{\gcd\left(a_{m},\prod_{j=i+1}^{m-1}b_j\right)}} \]

\(X=a_{m},Y=b_i,Z=\prod_{j=i+1}^{m-1}b_j\),则:

\[\frac{\gcd\left(a_{m},\prod_{j=i}^{m-1}b_j\right)}{\gcd\left(a_{m},\prod_{j=i+1}^{m-1}b_j\right)}=\frac{\gcd\left(X,YZ\right)}{\gcd\left(X,Z\right)} \]

\[=\gcd\left(\frac{X}{\gcd\left(X,Z\right)},Y\frac{Z}{\gcd\left(X,Z\right)}\right)=\gcd\left(\frac{X}{\gcd\left(X,Z\right)},Y\right)=\gcd\left(\frac{a_{m}}{\gcd\left(a_{m},\prod_{j=i+1}^{m-1}b_j\right)},b_i\right) \]

通过上述式子我们已经可以 \(O\left(Tn^2\log a\right)\) 求出答案。

\(s_i=\prod_{j=i}^{m-1}b_j\),注意到 \(\gcd\left(a_{m},\prod_{j=i+1}^{m-1}b_j\right)=\gcd\left(a_{m},s_{i+1}\right)\) 一定是 \(a_m\) 的约数,且对 \(\forall1\le i\lt j\le m\),有 \(\gcd\left(a_{m},s_j\right)|\gcd\left(a_{m},s_i\right)\),因此不同的 \(\gcd\left(a_{m},s_i\right)\) 取值只有 \(O\left(\log a\right)\) 种。

如何快速找到这 \(O\left(\log a\right)\) 个位置来减少计算 \(\gcd\) 的次数呢?注意到若 \(\gcd\left(a_{m},s_i\right)>\gcd\left(a_{m},s_{i+1}\right)\),那么一定存在某个质因子 \(p\) 在左式中的次数比右式中的高。

\(p\)\(a_{m},s_i,s_{i+1}\) 中的出现次数分别为 \(x,y,z\),那么 \(y>z\),又因为 \(\min\left(x,y\right)>\min\left(x,z\right)\),因此 \(x>z\)

\(\min\left(x,y\right)>z\),所以这等价于 \(\left(s_{i+1}\bmod a_m\right)\bmod \gcd\left(a_m,s_i\right)\ne0\)

因此,用一个变量 \(\text{tmp}\) 存当前 \(\gcd\left(a_m,s_i\right)\) 的值,根据 \(\left(s_{i+1}\bmod a_m\right)\bmod\text{tmp}\) 是否等于 \(0\) 可判断出是否需要更新 \(\text{tmp}\)

总时间复杂度 \(O\left(Tn\left(n+\log^2a\right)\right)\)

Code

#include<bits/stdc++.h>
#define LL long long
#define mod 1000000007
using namespace std;
int n,q,Test_num;
LL tmp,c,tmp1,tmp2;
LL a[302],b[302],s[302];
LL ans[302][302];
inline LL gcd(LL a,LL b)
{
	return b? gcd(b,a%b):a;
}
inline void solve()
{
	scanf("%d%d",&n,&q);
	for(int i=1;i<=n;++i)scanf("%lld",&a[i]);
	for(int i=1;i<=n;++i)
	{
		ans[i][i]=(b[i]=a[i])%mod,s[i]=1;
		for(int j=i-1;j;--j)s[j]=((__int128)s[j+1]*b[j])%a[i];
		tmp2=gcd(s[1],a[i]);
		for(int j=1;j<i;++j)if(s[j+1]%tmp2)tmp1=gcd(s[j+1],a[i]),c=tmp2/tmp1,tmp2=tmp1,tmp/=c,b[j]/=c;
		for(int j=i-1;j;--j)ans[j][i]=(ans[j+1][i]*(b[j]%mod))%mod;
	}
	for(int i=1,x,y;i<=q;++i)scanf("%d%d",&x,&y),printf("%lld\n",ans[x][y]);
}
int main()
{
	for(scanf("%d",&Test_num);Test_num--;)solve();
	return 0;
}
posted @ 2022-06-26 22:27  18Michael  阅读(91)  评论(0编辑  收藏  举报