题解:P6217 简单数论题
P6217 简单数论题题解
题目描述
给出一个长度为 \(n\) 的序列 \(a\),\(q\) 次询问 \(\prod_{i=l}^r \operatorname{lcm}(a_i,x)\) 的值。
本题思路很多,但先前的大佬们都并不注重代码上的说明,本篇题解着重代码实现。
公式结论
\[\prod_{i=l}^r \operatorname{lcm}(a_i,x)
=\frac{\prod_{i=l}^r a_i \times x^{r-l+1}}{\prod_{i=l}^r \operatorname{gcd}(ai,x)}
\]
\[\text{令 }x_i=\prod_{i=1}^k p_i^{q_i} \text{,其中 }p_i \text{是质数,} q_i \text{ 是自然数。}
\]
\[\prod_{i=l}^r \operatorname{gcd}(ai,x)
=\prod_{i=1}^k p_i^{\sum_{t=1}^{q_{i}} \sum_{i=l}^r [p_i^t|a_i]}
\]
其中 \(a \mid b\) 是整除,等价于 \(b \bmod a = 0\),\([]\) 指当其中表达式为真时返回 \(1\),否则返回 \(0\),推导过程请看其他大佬。
维护
维护 $\prod_{i=l}^r a_i $ 可用线段树,但码量大,不易写,考虑前缀积。
\[\prod_{i=l}^r a_i =\prod_{i=1}^r a_i \div \prod_{i=1}^{l-1} a_i
\]
除以一个数等价于乘这个数的逆元。
费马小定理:若 \(p\) 为素数,\(a\) 为正整数,且 \(a、p\) 互质。则有 \(a^{p−1} \equiv 1 \pmod p\)。
求 \(x^{r-l+1}\) 需快速幂。
观察上述分母公式,相当于求区间内集合中每个质数出现的次数,可以分解质因数。分解时考虑线性筛预处理,即代码中 \(w\) 数组。
每个质因数次幂建一个 vector,存 \(a_i\) 的下标,由于分解时有序,找 \(x\) 时二分找就行了。
code
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=2e5+5;
int mod=1e9+7;
int n,q;
int a[N];
int mul[N];
vector<int> v[N];
int w[N];
int g[N],s;
bool f[N];
int qpow(int x,int y){ //快速幂
int res=1;
while(y){
if(y&1) res=res*x%mod;
y>>=1;
x=x*x%mod;
}
return res;
}
signed main(){
f[1]=1; //线性筛
for(int i=2;i<N;i++){
if(!f[i]) g[++s]=i,w[i]=i;
for(int j=1;j<=s&&i*g[j]<N;j++){
f[i*g[j]]=1;
w[i*g[j]]=g[j];
if(i%g[j]==0) break;
}
}
cin>>n>>q; //读入
for(int i=1;i<=n;i++)
cin>>a[i];
mul[0]=1; // 前缀积
for(int i=1;i<=n;i++)
mul[i]=mul[i-1]*a[i]%mod;
for(int i=1;i<=n;i++){ //分解质因数
int x=a[i];
while(x>1){
int t=w[x],p=1;
while(x%t==0){
x/=t;
p*=t;
v[p].push_back(i);
}
}
}
while(q--){
int l,r,x;
cin>>l>>r>>x;
int ans=mul[r]*qpow(mul[l-1],mod-2)%mod;
//费马小定理:x^(mod-2) 是 x 在 mod 下的逆元
ans=ans*qpow(x,r-l+1)%mod;
while(x>1){
int t=w[x],p=1,sum=0;
while(x%t==0){
x/=t;
p*=t;
sum+=upper_bound(v[p].begin(),v[p].end(),r)-lower_bound(v[p].begin(),v[p].end(),l);
}
ans=ans*qpow(qpow(t,sum)%mod,mod-2)%mod;
}
cout<<ans<<'\n';
}
return 0;
}
完结撒花

浙公网安备 33010602011771号