@xiaoziyao
2021-08-15T08:41:27.000000Z
字数 1765
阅读 797
解题报告
P5655 基础数论函数练习题解题报告:
给定一个长度为 的序列, 次询问一个区间的 , 组数据。
。
这么小显然提示我们预处理所有答案,设 表示区间 的 。
考虑分治,当前分治到区间 ,中点为 ,那么我们要处理所有越过中点的区间的 。
发现我们可以根据 生成一个序列 ,满足 。
这个东西可以直接递推(很显然吧)
然后考虑经过 的区间的答案,我们固定左端点为 ,设当前右端点为 ,我们发现每次移动左端点的时候会让某些 失去某些质因子,也就是说我们可以在移动 的时候动态更新 的值。
具体,我们发现可以先求出 ,然后我们找这个 每一个因子是哪个位置贡献的,删去这个因子就好了,这样我们的时间复杂度就是 了。
有一个小细节,乘法需要使用玄学的快速乘,我也不知道这为什么是对的。
#include<stdio.h>
#define int long long
const int maxn=305,mod=1000000007;
int T,n,q;
int ans[maxn][maxn],a[maxn],b[maxn],c[maxn];
int gcd(int a,int b){
return b==0? a:gcd(b,a%b);
}
inline int getmul(int a,int b,int mod){
a%=mod,b%=mod;
long long res=a*b-(int)((long double)a/mod*b)*mod;
return res<0? res+mod:res;
}
void solve(int l,int r){
if(l==r){
ans[l][l]=a[l]%mod;
return;
}
int mid=(l+r)>>1;
solve(l,mid),solve(mid+1,r);
for(int i=mid;i>=l;i--){
int prod=1;
for(int j=i+1;j<=mid;j++)
prod=getmul(prod,b[j],a[i]);
b[i]=a[i]/gcd(a[i],prod);
}
for(int i=mid+1;i<=r;i++){
int prod=1;
for(int j=i-1;j>mid;j--)
prod=getmul(prod,b[j],a[i]);
b[i]=a[i]/gcd(a[i],prod);
}
int prod=1;
for(int i=mid;i>=l;i--){
prod=b[i]%mod*prod%mod;
c[mid]=1;
for(int j=mid+1;j<=r;j++)
c[j]=getmul(c[j-1],b[j],b[i]);
int g=gcd(c[r],b[i]);
for(int j=r;j>mid;j--)
if(c[j-1]%g){
int tmp=gcd(g,c[j-1]);
b[j]/=(g/tmp),g=tmp;
}
int PROD=prod;
for(int j=mid+1;j<=r;j++){
PROD=b[j]%mod*PROD%mod;
ans[i][j]=PROD;
}
}
}
signed main(){
scanf("%lld",&T);
while(T--){
scanf("%lld%lld",&n,&q);
for(int i=1;i<=n;i++)
scanf("%lld",&a[i]);
solve(1,n);
while(q--){
int x,y;
scanf("%lld%lld",&x,&y);
printf("%lld\n",ans[x][y]);
}
}
return 0;
}