Description
长度为N的正整数序列S,有Q次询问,每次询问一段区间内所有数的lcm(即最小公倍数)。由于答案可能很大,输出答案Mod 10^9 + 7。
例如:2 3 4 5,询问[1,3]区间的最小公倍数为2 3 4的最小公倍数 = 12。
Solution
一段数的最小公倍数中每个质数的指数就是这段数的这个质数出现的最大次数。
那么现在的问题就是怎么去维护一个质数的最大出现次数。
有很多组询问,我们考虑离线,把左端点和右端点分别设为第一,二关键字。然后限制左端点j(不断往右移),然后对于当前左端点是j的直接更新答案。
答案要怎么求?
有一个很机智的方法,就是把pk拆成p,p2,p3......,pk,然后放进一个数组里面,然后每次乘进一个权值p。当左端点右移的时候,就把对应的p的逆元除掉,如果当前p对应的pk在后面还有的话,那么再把p乘进去(所以要维护一个next)。因为p的加减至于k有关,所以可以维护最大值。
然后动态前缀积用树状数组就好了。
其实还可以用莫队强行辇过去。
Code
#include<iostream>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<math.h>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
typedef long long ll;
const int maxn=5e4+7,mo=1e9+7;
int i,j,k,n,m,ans[maxn],q,x,tot,a[maxn*10],b[maxn*10],c[maxn],l[maxn*10],r[maxn*10],next[maxn*10],d[maxn];
ll t[maxn*10];
bool bz[maxn];
struct node{
int l,r,c;
}wen[maxn];
bool cmp(node x,node y){return x.l<y.l||x.l==y.l&&x.r<y.r;}
ll qsm(ll x,ll y){
ll z=1;
for(;y;y/=2,x=x*x%mo)if(y&1)z=z*x%mo;
return z;
}
int lowbit(int x){return x&-x;}
void add(int x,ll y){
for(;x<=tot;x+=lowbit(x))t[x]=t[x]*y%mo;
}
ll find(int x){
ll z=1;
for(;x;x-=lowbit(x))z=z*t[x]%mo;
return z;
}
int main(){
freopen("fan.in","r",stdin);
scanf("%d%d",&n,&q);
fo(i,1,n){
scanf("%d",&c[i]);x=c[i];l[i]=tot+1;
fo(j,2,sqrt(c[i])){
k=1;
while(x%j==0)k*=j,x/=j,a[++tot]=k,b[tot]=j;
}
if(x!=1)a[++tot]=b[tot]=x;
r[i]=tot;
}
fo(i,1,tot)t[i]=1;
fo(i,1,tot){
if(d[a[i]])next[d[a[i]]]=i;
else add(i,b[i]);
d[a[i]]=i;
}
fo(i,1,q)scanf("%d%d",&wen[i].l,&wen[i].r),wen[i].c=i,wen[i].l=l[wen[i].l],wen[i].r=r[wen[i].r];
sort(wen+1,wen+1+q,cmp);j=0;
fo(i,1,tot){
while(wen[j+1].l==i)ans[wen[++j].c]=find(wen[j].r);
add(i,qsm(b[i],mo-2));
if(next[i])add(next[i],b[i]);
}
fo(i,1,q)printf("%d\n",ans[i]);
}