题目链接:点击查看
题目大意:给出 n 个数组成的数组 a ,再给出 m 次询问,每次询问需要回答区间 [ l , r ] 中所有元素的最小公倍数,强制在线
题目分析:首先考虑多个数的最小公倍数该如何求,因为每个质因子对于最小公倍数的贡献都是相互独立的,所以对于某个质因子 p 来说,可以求出其在所有数字中出现的次数记为 b[ 1 ] , b[ 2 ] ... b[ n ] ,那么质因子 p 对于最小公倍数的贡献就是 ,对于所有质因子的贡献求乘积就是所求的最小公倍数了
因为每个元素只有 2e5 这么大,所以考虑开根号,令阈值等于 500,满足 500 * 500 >= 2e5 ,其意义就是,在某个数字中,出现次数大于等于 2 的质因子的大小,一定不会大于 500,这样一来我们就可以将所有的质因子分两类单独计算贡献了:
- 如果某个质因子 p 小于等于 500,按照上文的思路,维护出现次数的区间最大值即可
- 如果某个质因子 p 大于 500,其贡献非 0 即 1,又因为多个质数的最小公倍数就是其乘积,计算一下区间内有多少个不同的质数,并维护其乘积即可
对于第一种情况,因为 500 以内的质数只有 95 个,所以可以直接开 95 个线段树去维护每个质因子的贡献,维护一下区间最大值即可,对于第二种情况,统计区间内不同的质因子,不难想到使用主席树维护,套上模板随便改改就好了
这可能不是正解,因为递归式的线段树写 T 了,换成循环式的线段树勉强划过去了
代码:
//#pragma GCC optimize(2)
//#pragma GCC optimize("Ofast","inline","-ffast-math")
//#pragma GCC target("avx,sse2,sse3,sse4,mmx")
#include<iostream>
#include<cstdio>
#include<string>
#include<ctime>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<stack>
#include<climits>
#include<queue>
#include<map>
#include<set>
#include<sstream>
#include<cassert>
#include<bitset>
#include<list>
using namespace std;
typedef long long LL;
typedef unsigned long long ull;
const int inf=0x3f3f3f3f;
const int N=1e5+100;
const int mod=1e9+7;
int num[N],a[N],last[N<<1],n,seg_cnt;
/*线段树*/
struct Seg_tree
{
int mmax[262626];
int base=131072;
void build()
{
for(int i=0;i<n;i++)
mmax[base+i]=num[i+1];
for(int i=base-1;i>=1;i--)
mmax[i]=max(mmax[i<<1],mmax[i<<1|1]);
}
int query(int k,int l,int r)
{
l+=base;r+=base;
l--;r--;
int ans=0;
while(l<=r)
{
if(l&1)
{
ans=max(ans,mmax[l]);
l++;
}
if(~r&1)
{
ans=max(ans,mmax[r]);
r--;
}
l>>=1;r>>=1;
}
return ans;
}
}seg[105];
/*线段树*/
/*主席树*/
struct Node
{
int l,r;
LL sum;
}tree[N*40];
int cnt,root[N];
void update(int pos,int &k,int l,int r,LL val)
{
tree[cnt++]=tree[k];
k=cnt-1;
tree[k].sum=max(tree[k].sum,1LL)*val%mod;
if(l==r)
return;
int mid=l+r>>1;
if(pos<=mid)
update(pos,tree[k].l,l,mid,val);
else
update(pos,tree[k].r,mid+1,r,val);
}
LL query(int rt,int l,int r,int L,int R)//[l,r]:目标区间,[L,R]:当前区间
{
if(R<l||L>r)
return 1;
if(L>=l&&R<=r)
return max(tree[rt].sum,1LL);
int mid=L+R>>1;
return query(tree[rt].l,l,r,L,mid)*query(tree[rt].r,l,r,mid+1,R)%mod;
}
void init()
{
root[0]=0;
tree[0].l=tree[0].r=tree[0].sum=0;
cnt=1;
}
/*主席树*/
/*快速幂+逆元*/
LL q_pow(LL a,LL b)
{
LL ans=1;
while(b)
{
if(b&1)
ans=ans*a%mod;
a=a*a%mod;
b>>=1;
}
return ans;
}
LL inv(LL num)
{
return q_pow(num,mod-2);
}
/*快速幂+逆元*/
/*埃氏筛*/
bool vis[N<<1];
void P()
{
for(int i=2;i<=500;i++)
{
if(vis[i])
continue;
for(int j=1;j<=n;j++)
{
num[j]=1;
while(a[j]%i==0)
{
a[j]/=i;
num[j]*=i;
}
}
seg_cnt++;
seg[seg_cnt].build();
for(int j=i+i;j<N<<1;j+=i)
vis[j]=true;
}
}
/*埃氏筛*/
int main()
{
#ifndef ONLINE_JUDGE
// freopen("data.in.txt","r",stdin);
// freopen("data.out.txt","w",stdout);
#endif
// ios::sync_with_stdio(false);
init();
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",a+i);
P();
//到此为止剩下的a[i]都是大于500的素数了
for(int i=1;i<=n;i++)
{
root[i]=root[i-1];
if(a[i]==1)
continue;
if(last[a[i]])
update(last[a[i]],root[i],1,n,inv(a[i]));
update(i,root[i],1,n,a[i]);
last[a[i]]=i;
}
int m;
scanf("%d",&m);
LL last=0;
while(m--)
{
int l,r;
scanf("%d%d",&l,&r);
l=(l+last)%n+1;
r=(r+last)%n+1;
if(l>r)
swap(l,r);
LL ans=1;
for(int i=1;i<=seg_cnt;i++)
ans=ans*seg[i].query(1,l,r)%mod;
ans=ans*query(root[r],l,r,1,n)%mod;
printf("%lld\n",last=ans);
}
return 0;
}