Boring Queries
Boring Queries
分析
关键点之一,在于我们对lcm
性质的了解。n个数的lcm
就等于\(LCM_{i=l}^{r}a_i=\prod_{p是质数}p^{\max_{l\leq i \leq r}}cntt_{i,p}\),其中\(cnt_{i,p}\)表示\(a_i\)中p
的的次数。
我们考虑根号分治。小于450
的质数有87
个。
下面就是一步很精妙的想法,我们先将所有\(a_i\)中的小于450
的质因子全部除去,则剩下的一定是1
或者是大于450
的质数。
则我们的答案ans
首先就会来自一部分在小于450
的质数中,我们先把那一部分统计下来,由于我们要求区间内数字的区间最值,还是静态的,那当然用ST
表来实现。我们用ST
表去维护,每一个数字在某个区间出现的最大次数。
然后剩下的问题就变成了,求区间[l,r]
中出现过的数字的乘积(相同的只算一次)。粘上去重我们一般都考虑能否离线,但是这题强制在线。如果不能离线,那这类问题我们通常用主席树
去做。我们来看看怎么用主席树去做。
首先,我们设\(pre_i\)表示[1,i-1]
中\(a_i\)最后一次出现的位置,没有出现则为0。这个可以\(O(n)\)求到。
对于一个\(i(l \leq i \leq r)\),若\(l \leq pre_i\),说明[l,i-1]
中出现了一个与\(a_i\)相等的数,那么这个i
是不能算贡献的。
问题就又变成了,\(\prod_{i=l}^{r}[pre_i \leq l - 1]a_i\)。考虑建立主席树,第j
棵树维护的是\(pre_i \leq j\)的数的乘积。(\(peq_i>j\)的位置上都为1)。
我们需要做的是,对于某个值\(a_i\),如果是第一次出现,我们直接在root[0]
的位置进行修改。否则,我们设\(b_i\)表示为下一个与\(a_i\)相同的位置是哪,我们到这个位置后,直接新建一棵树,直接单点修\(b_i\),该位置改为\(a_{b_i}\)。
简单来说,我们是用这种方式,用空间把所有情况预处理出来了。所有的本可以离线但强制在线的题目都是类似的思路。
看看代码。
AC_code
#include<bits/stdc++.h>
#define ios ios::sync_with_stdio(false); cin.tie(0), cout.tie(0)
using namespace std;
using ll = long long;
const int N = 2e5 + 10,SQRT = 450,mod = 1e9 + 7;
struct Node
{
int l,r;
ll prod;
}tr[N*20];
int n,a[N],b[N],rt[N],idx,lg[N],primes[SQRT],pos[N],cnt;
bool ispri[SQRT];
char st[90][N][18];
ll poww[100][20];
void get_primes()
{
ispri[1] = 1;
for(int i=2;i<=SQRT-1;i++)
{
if(!ispri[i]) primes[++cnt] = i;
for(int j=1;primes[j]*i<=SQRT-1;j++)
{
ispri[primes[j]*i] = 1;
if(i%primes[j]==0) break;
}
}
}
void pushup(int u)
{
tr[u].prod = tr[tr[u].l].prod * tr[tr[u].r].prod % mod;
}
int query(int u,int l,int r)
{
int p = lg[r - l + 1];
return max(st[u][l][p],st[u][r-(1<<p)+1][p]);
}
void modify1(int &u,int l,int r,int x,int y)
{
if(l==r)
{
tr[u].prod = y;
return ;
}
int mid = l + r >> 1;
if(x<=mid) modify1(tr[u].l,l,mid,x,y);
else modify1(tr[u].r,mid+1,r,x,y);
pushup(u);
}
void modify2(int u,int &v,int l,int r,int x,int y)
{
v = ++idx;
tr[v] = tr[u];
if(l==r)
{
tr[v].prod = y;
return ;
}
int mid = l + r >> 1;
if(x<=mid) modify2(tr[u].l,tr[v].l,l,mid,x,y);
else modify2(tr[u].r,tr[v].r,mid+1,r,x,y);
pushup(v);
}
ll query(int u,int l,int r,int x,int y)
{
if(l>y||r<x) return 1;
if(!u) return 1;
if(x<=l&&r<=y) return tr[u].prod;
int mid = l + r >> 1;
return query(tr[u].l,l,mid,x,y) * query(tr[u].r,mid+1,r,x,y) % mod;
}
void build(int &u,int l,int r)
{
u = ++idx;
if(l==r)
{
tr[u].prod = 1;
return ;
}
int mid = l + r >> 1;
build(tr[u].l,l,mid),build(tr[u].r,mid+1,r);
pushup(u);
}
void buildst()
{
for(int i=2;i<=n;i++) lg[i] = lg[i/2] + 1;
for(int k=1;k<=cnt;k++)
for(int j=1;j<=lg[n];j++)
for(int i=1;i+(1<<j)-1<=n;i++)
st[k][i][j] = max(st[k][i][j-1],st[k][i+(1<<j-1)][j-1]);
}
void buildseg()
{
build(rt[0],1,n);
for(int i=1;i<=n;i++)
{
if(!pos[a[i]]) modify1(rt[0],1,n,i,a[i]);
else b[pos[a[i]]] = i;
pos[a[i]] = i;
}
for(int i=1;i<=n;i++)
{
if(b[i]) modify2(rt[i-1],rt[i],1,n,b[i],a[b[i]]);
else rt[i] = rt[i-1];
}
}
int main()
{
get_primes();
ios;cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i];
for(int j=1;j<=cnt;j++)
while(a[i]%primes[j]==0)
{
a[i]/=primes[j];
st[j][i][0]++;
}
}
buildst();
buildseg();
ll ans = 0;
for(int i=1;i<=cnt;i++)
{
poww[i][0] = 1;
for(int j=1;j<=20;j++) poww[i][j] = (poww[i][j-1]*primes[i])%mod;
}
int q;cin>>q;
while(q--)
{
int x,y;cin>>x>>y;
x = (x + ans) % n + 1;y = (y + ans) %n + 1;
if(x>y) swap(x,y);
ans = 1;
for(int i=1;i<=cnt;i++) ans = ans * poww[i][query(i,x,y)] % mod;
ans = ans * query(rt[x-1],1,n,x,y) % mod;
cout<<ans<<'\n';
}
return 0;
}